auth
auth/authcode.go
Enable the implict grant flow. Add the implicit grant flow. This can't be done in a grant type, because it's not specified through the grant_type parameter, for some absurd reason. Whatever. We basically achieved this by refactoring how we respond to the authorization endpoint, keying off the "response_type" parameter.
| 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@84 | 18 }) |
| paddy@84 | 19 } |
| paddy@84 | 20 |
| paddy@29 | 21 var ( |
| paddy@87 | 22 // ErrNoAuthorizationCodeStore is returned when a Context tries to act on a authorizationCodeStore without setting one first. |
| paddy@87 | 23 ErrNoAuthorizationCodeStore = errors.New("no authorizationCodeStore was specified for the Context") |
| paddy@87 | 24 // ErrAuthorizationCodeNotFound is returned when an AuthorizationCode is requested but not found in the authorizationCodeStore. |
| paddy@87 | 25 ErrAuthorizationCodeNotFound = errors.New("authorization code not found in authorizationCodeStore") |
| paddy@87 | 26 // ErrAuthorizationCodeAlreadyExists is returned when an AuthorizationCode is added to a authorizationCodeStore, but another AuthorizationCode with the |
| paddy@87 | 27 // same Code already exists in the authorizationCodeStore. |
| paddy@87 | 28 ErrAuthorizationCodeAlreadyExists = errors.New("authorization code already exists in authorizationCodeStore") |
| paddy@29 | 29 ) |
| paddy@29 | 30 |
| paddy@87 | 31 // AuthorizationCode represents an authorization grant made by a user to a Client, to |
| paddy@57 | 32 // access user data within a defined Scope for a limited amount of time. |
| paddy@87 | 33 type AuthorizationCode struct { |
| paddy@26 | 34 Code string |
| paddy@26 | 35 Created time.Time |
| paddy@26 | 36 ExpiresIn int32 |
| paddy@26 | 37 ClientID uuid.ID |
| paddy@26 | 38 Scope string |
| paddy@26 | 39 RedirectURI string |
| paddy@26 | 40 State string |
| paddy@69 | 41 ProfileID uuid.ID |
| paddy@94 | 42 Used bool |
| paddy@26 | 43 } |
| paddy@26 | 44 |
| paddy@87 | 45 type authorizationCodeStore interface { |
| paddy@87 | 46 getAuthorizationCode(code string) (AuthorizationCode, error) |
| paddy@87 | 47 saveAuthorizationCode(authCode AuthorizationCode) error |
| paddy@87 | 48 deleteAuthorizationCode(code string) error |
| paddy@94 | 49 useAuthorizationCode(code string) error |
| paddy@26 | 50 } |
| paddy@29 | 51 |
| paddy@87 | 52 func (m *memstore) getAuthorizationCode(code string) (AuthorizationCode, error) { |
| paddy@87 | 53 m.authCodeLock.RLock() |
| paddy@87 | 54 defer m.authCodeLock.RUnlock() |
| paddy@87 | 55 authCode, ok := m.authCodes[code] |
| paddy@29 | 56 if !ok { |
| paddy@87 | 57 return AuthorizationCode{}, ErrAuthorizationCodeNotFound |
| paddy@29 | 58 } |
| paddy@87 | 59 return authCode, nil |
| paddy@29 | 60 } |
| paddy@29 | 61 |
| paddy@87 | 62 func (m *memstore) saveAuthorizationCode(authCode AuthorizationCode) error { |
| paddy@87 | 63 m.authCodeLock.Lock() |
| paddy@87 | 64 defer m.authCodeLock.Unlock() |
| paddy@87 | 65 _, ok := m.authCodes[authCode.Code] |
| paddy@29 | 66 if ok { |
| paddy@87 | 67 return ErrAuthorizationCodeAlreadyExists |
| paddy@29 | 68 } |
| paddy@87 | 69 m.authCodes[authCode.Code] = authCode |
| paddy@29 | 70 return nil |
| paddy@29 | 71 } |
| paddy@29 | 72 |
| paddy@87 | 73 func (m *memstore) deleteAuthorizationCode(code string) error { |
| paddy@87 | 74 m.authCodeLock.Lock() |
| paddy@87 | 75 defer m.authCodeLock.Unlock() |
| paddy@87 | 76 _, ok := m.authCodes[code] |
| paddy@29 | 77 if !ok { |
| paddy@87 | 78 return ErrAuthorizationCodeNotFound |
| paddy@29 | 79 } |
| paddy@87 | 80 delete(m.authCodes, code) |
| paddy@29 | 81 return nil |
| paddy@29 | 82 } |
| paddy@84 | 83 |
| paddy@94 | 84 func (m *memstore) useAuthorizationCode(code string) error { |
| paddy@94 | 85 m.authCodeLock.Lock() |
| paddy@94 | 86 defer m.authCodeLock.Unlock() |
| paddy@94 | 87 a, ok := m.authCodes[code] |
| paddy@94 | 88 if !ok { |
| paddy@94 | 89 return ErrAuthorizationCodeNotFound |
| paddy@94 | 90 } |
| paddy@94 | 91 a.Used = true |
| paddy@94 | 92 m.authCodes[code] = a |
| paddy@94 | 93 return nil |
| paddy@94 | 94 } |
| paddy@94 | 95 |
| paddy@84 | 96 func authCodeGrantValidate(w http.ResponseWriter, r *http.Request, context Context) (scope string, profileID uuid.ID, valid bool) { |
| paddy@84 | 97 enc := json.NewEncoder(w) |
| paddy@84 | 98 code := r.PostFormValue("code") |
| paddy@84 | 99 if code == "" { |
| paddy@84 | 100 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 101 renderJSONError(enc, "invalid_request") |
| paddy@84 | 102 return |
| paddy@84 | 103 } |
| paddy@85 | 104 clientID, success := verifyClient(w, r, true, context) |
| paddy@85 | 105 if !success { |
| paddy@84 | 106 return |
| paddy@84 | 107 } |
| paddy@87 | 108 authCode, err := context.GetAuthorizationCode(code) |
| paddy@84 | 109 if err != nil { |
| paddy@87 | 110 if err == ErrAuthorizationCodeNotFound { |
| paddy@84 | 111 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 112 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 113 return |
| paddy@84 | 114 } |
| paddy@84 | 115 w.WriteHeader(http.StatusInternalServerError) |
| paddy@84 | 116 renderJSONError(enc, "server_error") |
| paddy@84 | 117 return |
| paddy@84 | 118 } |
| paddy@85 | 119 redirectURI := r.PostFormValue("redirect_uri") |
| paddy@87 | 120 if authCode.RedirectURI != redirectURI { |
| paddy@84 | 121 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 122 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 123 return |
| paddy@84 | 124 } |
| paddy@87 | 125 if !authCode.ClientID.Equal(clientID) { |
| paddy@84 | 126 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 127 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 128 return |
| paddy@84 | 129 } |
| paddy@87 | 130 return authCode.Scope, authCode.ProfileID, true |
| paddy@84 | 131 } |
| paddy@90 | 132 |
| paddy@90 | 133 func authCodeGrantInvalidate(r *http.Request, context Context) error { |
| paddy@94 | 134 code := r.PostFormValue("code") |
| paddy@94 | 135 if code == "" { |
| paddy@94 | 136 return ErrAuthorizationCodeNotFound |
| paddy@94 | 137 } |
| paddy@94 | 138 return context.UseAuthorizationCode(code) |
| paddy@90 | 139 } |