auth

Paddy 2015-06-29 Parent:cf1aef6eb81f Child:b7e685839a1b

174:9e3ceddf29ad Go to Latest

auth/authcode.go

Use an environment variable to set the JWT secret. When setting up the authd server, populate the JWT secret using a JWT_SECRET environment variable. Incidentally, we also included the subscriptions scope, for testing purposes while creating code.secondbit.org/ducky/subscriptions. We now also log the port we're listening on, listen on all interfaces (instead of just 127.0.0.1), and changed the port to 9000 instead of 8080.

History
paddy@26 1 package auth
paddy@26 2
paddy@26 3 import (
paddy@84 4 "encoding/json"
paddy@29 5 "errors"
paddy@84 6 "net/http"
paddy@26 7 "time"
paddy@26 8
paddy@107 9 "code.secondbit.org/uuid.hg"
paddy@26 10 )
paddy@26 11
paddy@84 12 func init() {
paddy@84 13 RegisterGrantType("authorization_code", GrantType{
paddy@84 14 Validate: authCodeGrantValidate,
paddy@94 15 Invalidate: authCodeGrantInvalidate,
paddy@84 16 IssuesRefresh: true,
paddy@85 17 ReturnToken: RenderJSONToken,
paddy@123 18 AllowsPublic: true,
paddy@124 19 AuditString: authCodeGrantAuditString,
paddy@84 20 })
paddy@84 21 }
paddy@84 22
paddy@29 23 var (
paddy@87 24 // ErrNoAuthorizationCodeStore is returned when a Context tries to act on a authorizationCodeStore without setting one first.
paddy@87 25 ErrNoAuthorizationCodeStore = errors.New("no authorizationCodeStore was specified for the Context")
paddy@87 26 // ErrAuthorizationCodeNotFound is returned when an AuthorizationCode is requested but not found in the authorizationCodeStore.
paddy@87 27 ErrAuthorizationCodeNotFound = errors.New("authorization code not found in authorizationCodeStore")
paddy@87 28 // ErrAuthorizationCodeAlreadyExists is returned when an AuthorizationCode is added to a authorizationCodeStore, but another AuthorizationCode with the
paddy@87 29 // same Code already exists in the authorizationCodeStore.
paddy@87 30 ErrAuthorizationCodeAlreadyExists = errors.New("authorization code already exists in authorizationCodeStore")
paddy@29 31 )
paddy@29 32
paddy@87 33 // AuthorizationCode represents an authorization grant made by a user to a Client, to
paddy@57 34 // access user data within a defined Scope for a limited amount of time.
paddy@87 35 type AuthorizationCode struct {
paddy@26 36 Code string
paddy@26 37 Created time.Time
paddy@26 38 ExpiresIn int32
paddy@26 39 ClientID uuid.ID
paddy@163 40 Scopes Scopes
paddy@26 41 RedirectURI string
paddy@26 42 State string
paddy@69 43 ProfileID uuid.ID
paddy@94 44 Used bool
paddy@26 45 }
paddy@26 46
paddy@87 47 type authorizationCodeStore interface {
paddy@87 48 getAuthorizationCode(code string) (AuthorizationCode, error)
paddy@87 49 saveAuthorizationCode(authCode AuthorizationCode) error
paddy@87 50 deleteAuthorizationCode(code string) error
paddy@163 51 deleteAuthorizationCodesByProfileID(profileID uuid.ID) error
paddy@164 52 deleteAuthorizationCodesByClientID(clientID uuid.ID) error
paddy@94 53 useAuthorizationCode(code string) error
paddy@26 54 }
paddy@29 55
paddy@87 56 func (m *memstore) getAuthorizationCode(code string) (AuthorizationCode, error) {
paddy@87 57 m.authCodeLock.RLock()
paddy@87 58 defer m.authCodeLock.RUnlock()
paddy@87 59 authCode, ok := m.authCodes[code]
paddy@29 60 if !ok {
paddy@87 61 return AuthorizationCode{}, ErrAuthorizationCodeNotFound
paddy@29 62 }
paddy@87 63 return authCode, nil
paddy@29 64 }
paddy@29 65
paddy@87 66 func (m *memstore) saveAuthorizationCode(authCode AuthorizationCode) error {
paddy@87 67 m.authCodeLock.Lock()
paddy@87 68 defer m.authCodeLock.Unlock()
paddy@87 69 _, ok := m.authCodes[authCode.Code]
paddy@29 70 if ok {
paddy@87 71 return ErrAuthorizationCodeAlreadyExists
paddy@29 72 }
paddy@87 73 m.authCodes[authCode.Code] = authCode
paddy@29 74 return nil
paddy@29 75 }
paddy@29 76
paddy@87 77 func (m *memstore) deleteAuthorizationCode(code string) error {
paddy@87 78 m.authCodeLock.Lock()
paddy@87 79 defer m.authCodeLock.Unlock()
paddy@87 80 _, ok := m.authCodes[code]
paddy@29 81 if !ok {
paddy@87 82 return ErrAuthorizationCodeNotFound
paddy@29 83 }
paddy@87 84 delete(m.authCodes, code)
paddy@29 85 return nil
paddy@29 86 }
paddy@84 87
paddy@163 88 func (m *memstore) deleteAuthorizationCodesByProfileID(profileID uuid.ID) error {
paddy@163 89 m.authCodeLock.Lock()
paddy@163 90 defer m.authCodeLock.Unlock()
paddy@163 91 var codes []string
paddy@163 92 for _, code := range m.authCodes {
paddy@163 93 if code.ProfileID.Equal(profileID) {
paddy@163 94 codes = append(codes, code.Code)
paddy@163 95 }
paddy@163 96 }
paddy@163 97 if len(codes) < 1 {
paddy@163 98 return ErrProfileNotFound
paddy@163 99 }
paddy@163 100 for _, code := range codes {
paddy@163 101 delete(m.authCodes, code)
paddy@163 102 }
paddy@163 103 return nil
paddy@163 104 }
paddy@163 105
paddy@164 106 func (m *memstore) deleteAuthorizationCodesByClientID(clientID uuid.ID) error {
paddy@164 107 m.authCodeLock.Lock()
paddy@164 108 defer m.authCodeLock.Unlock()
paddy@164 109 var codes []string
paddy@164 110 for _, code := range m.authCodes {
paddy@164 111 if code.ClientID.Equal(clientID) {
paddy@164 112 codes = append(codes, code.Code)
paddy@164 113 }
paddy@164 114 }
paddy@164 115 if len(codes) < 1 {
paddy@164 116 return ErrClientNotFound
paddy@164 117 }
paddy@164 118 for _, code := range codes {
paddy@164 119 delete(m.authCodes, code)
paddy@164 120 }
paddy@164 121 return nil
paddy@164 122 }
paddy@164 123
paddy@94 124 func (m *memstore) useAuthorizationCode(code string) error {
paddy@94 125 m.authCodeLock.Lock()
paddy@94 126 defer m.authCodeLock.Unlock()
paddy@94 127 a, ok := m.authCodes[code]
paddy@94 128 if !ok {
paddy@94 129 return ErrAuthorizationCodeNotFound
paddy@94 130 }
paddy@94 131 a.Used = true
paddy@94 132 m.authCodes[code] = a
paddy@94 133 return nil
paddy@94 134 }
paddy@94 135
paddy@163 136 func authCodeGrantValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes Scopes, profileID uuid.ID, valid bool) {
paddy@84 137 enc := json.NewEncoder(w)
paddy@84 138 code := r.PostFormValue("code")
paddy@84 139 if code == "" {
paddy@84 140 w.WriteHeader(http.StatusBadRequest)
paddy@84 141 renderJSONError(enc, "invalid_request")
paddy@84 142 return
paddy@84 143 }
paddy@123 144 clientID, _, ok := getClientAuth(w, r, true)
paddy@123 145 if !ok {
paddy@84 146 return
paddy@84 147 }
paddy@87 148 authCode, err := context.GetAuthorizationCode(code)
paddy@84 149 if err != nil {
paddy@87 150 if err == ErrAuthorizationCodeNotFound {
paddy@84 151 w.WriteHeader(http.StatusBadRequest)
paddy@84 152 renderJSONError(enc, "invalid_grant")
paddy@84 153 return
paddy@84 154 }
paddy@84 155 w.WriteHeader(http.StatusInternalServerError)
paddy@84 156 renderJSONError(enc, "server_error")
paddy@84 157 return
paddy@84 158 }
paddy@85 159 redirectURI := r.PostFormValue("redirect_uri")
paddy@87 160 if authCode.RedirectURI != redirectURI {
paddy@84 161 w.WriteHeader(http.StatusBadRequest)
paddy@84 162 renderJSONError(enc, "invalid_grant")
paddy@84 163 return
paddy@84 164 }
paddy@87 165 if !authCode.ClientID.Equal(clientID) {
paddy@84 166 w.WriteHeader(http.StatusBadRequest)
paddy@84 167 renderJSONError(enc, "invalid_grant")
paddy@84 168 return
paddy@84 169 }
paddy@135 170 return authCode.Scopes, authCode.ProfileID, true
paddy@84 171 }
paddy@90 172
paddy@90 173 func authCodeGrantInvalidate(r *http.Request, context Context) error {
paddy@94 174 code := r.PostFormValue("code")
paddy@94 175 if code == "" {
paddy@94 176 return ErrAuthorizationCodeNotFound
paddy@94 177 }
paddy@94 178 return context.UseAuthorizationCode(code)
paddy@90 179 }
paddy@124 180
paddy@124 181 func authCodeGrantAuditString(r *http.Request) string {
paddy@124 182 return "authcode:" + r.PostFormValue("code")
paddy@124 183 }