auth
auth/authcode.go
Implement revoking tokens. Add a revokeToken method to tokenStore, and an implementation for memstore. All it does is set the token's Revoked property to true.
| 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@45 | 9 "code.secondbit.org/uuid" |
| paddy@26 | 10 ) |
| paddy@26 | 11 |
| paddy@84 | 12 func init() { |
| paddy@84 | 13 RegisterGrantType("authorization_code", GrantType{ |
| paddy@84 | 14 Validate: authCodeGrantValidate, |
| paddy@84 | 15 IssuesRefresh: true, |
| paddy@85 | 16 ReturnToken: RenderJSONToken, |
| paddy@84 | 17 }) |
| paddy@84 | 18 } |
| paddy@84 | 19 |
| paddy@29 | 20 var ( |
| paddy@87 | 21 // ErrNoAuthorizationCodeStore is returned when a Context tries to act on a authorizationCodeStore without setting one first. |
| paddy@87 | 22 ErrNoAuthorizationCodeStore = errors.New("no authorizationCodeStore was specified for the Context") |
| paddy@87 | 23 // ErrAuthorizationCodeNotFound is returned when an AuthorizationCode is requested but not found in the authorizationCodeStore. |
| paddy@87 | 24 ErrAuthorizationCodeNotFound = errors.New("authorization code not found in authorizationCodeStore") |
| paddy@87 | 25 // ErrAuthorizationCodeAlreadyExists is returned when an AuthorizationCode is added to a authorizationCodeStore, but another AuthorizationCode with the |
| paddy@87 | 26 // same Code already exists in the authorizationCodeStore. |
| paddy@87 | 27 ErrAuthorizationCodeAlreadyExists = errors.New("authorization code already exists in authorizationCodeStore") |
| paddy@29 | 28 ) |
| paddy@29 | 29 |
| paddy@87 | 30 // AuthorizationCode represents an authorization grant made by a user to a Client, to |
| paddy@57 | 31 // access user data within a defined Scope for a limited amount of time. |
| paddy@87 | 32 type AuthorizationCode struct { |
| paddy@26 | 33 Code string |
| paddy@26 | 34 Created time.Time |
| paddy@26 | 35 ExpiresIn int32 |
| paddy@26 | 36 ClientID uuid.ID |
| paddy@26 | 37 Scope string |
| paddy@26 | 38 RedirectURI string |
| paddy@26 | 39 State string |
| paddy@69 | 40 ProfileID uuid.ID |
| paddy@26 | 41 } |
| paddy@26 | 42 |
| paddy@87 | 43 type authorizationCodeStore interface { |
| paddy@87 | 44 getAuthorizationCode(code string) (AuthorizationCode, error) |
| paddy@87 | 45 saveAuthorizationCode(authCode AuthorizationCode) error |
| paddy@87 | 46 deleteAuthorizationCode(code string) error |
| paddy@26 | 47 } |
| paddy@29 | 48 |
| paddy@87 | 49 func (m *memstore) getAuthorizationCode(code string) (AuthorizationCode, error) { |
| paddy@87 | 50 m.authCodeLock.RLock() |
| paddy@87 | 51 defer m.authCodeLock.RUnlock() |
| paddy@87 | 52 authCode, ok := m.authCodes[code] |
| paddy@29 | 53 if !ok { |
| paddy@87 | 54 return AuthorizationCode{}, ErrAuthorizationCodeNotFound |
| paddy@29 | 55 } |
| paddy@87 | 56 return authCode, nil |
| paddy@29 | 57 } |
| paddy@29 | 58 |
| paddy@87 | 59 func (m *memstore) saveAuthorizationCode(authCode AuthorizationCode) error { |
| paddy@87 | 60 m.authCodeLock.Lock() |
| paddy@87 | 61 defer m.authCodeLock.Unlock() |
| paddy@87 | 62 _, ok := m.authCodes[authCode.Code] |
| paddy@29 | 63 if ok { |
| paddy@87 | 64 return ErrAuthorizationCodeAlreadyExists |
| paddy@29 | 65 } |
| paddy@87 | 66 m.authCodes[authCode.Code] = authCode |
| paddy@29 | 67 return nil |
| paddy@29 | 68 } |
| paddy@29 | 69 |
| paddy@87 | 70 func (m *memstore) deleteAuthorizationCode(code string) error { |
| paddy@87 | 71 m.authCodeLock.Lock() |
| paddy@87 | 72 defer m.authCodeLock.Unlock() |
| paddy@87 | 73 _, ok := m.authCodes[code] |
| paddy@29 | 74 if !ok { |
| paddy@87 | 75 return ErrAuthorizationCodeNotFound |
| paddy@29 | 76 } |
| paddy@87 | 77 delete(m.authCodes, code) |
| paddy@29 | 78 return nil |
| paddy@29 | 79 } |
| paddy@84 | 80 |
| paddy@84 | 81 func authCodeGrantValidate(w http.ResponseWriter, r *http.Request, context Context) (scope string, profileID uuid.ID, valid bool) { |
| paddy@84 | 82 enc := json.NewEncoder(w) |
| paddy@84 | 83 code := r.PostFormValue("code") |
| paddy@84 | 84 if code == "" { |
| paddy@84 | 85 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 86 renderJSONError(enc, "invalid_request") |
| paddy@84 | 87 return |
| paddy@84 | 88 } |
| paddy@85 | 89 clientID, success := verifyClient(w, r, true, context) |
| paddy@85 | 90 if !success { |
| paddy@84 | 91 return |
| paddy@84 | 92 } |
| paddy@87 | 93 authCode, err := context.GetAuthorizationCode(code) |
| paddy@84 | 94 if err != nil { |
| paddy@87 | 95 if err == ErrAuthorizationCodeNotFound { |
| paddy@84 | 96 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 97 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 98 return |
| paddy@84 | 99 } |
| paddy@84 | 100 w.WriteHeader(http.StatusInternalServerError) |
| paddy@84 | 101 renderJSONError(enc, "server_error") |
| paddy@84 | 102 return |
| paddy@84 | 103 } |
| paddy@85 | 104 redirectURI := r.PostFormValue("redirect_uri") |
| paddy@87 | 105 if authCode.RedirectURI != redirectURI { |
| paddy@84 | 106 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 107 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 108 return |
| paddy@84 | 109 } |
| paddy@87 | 110 if !authCode.ClientID.Equal(clientID) { |
| paddy@84 | 111 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 112 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 113 return |
| paddy@84 | 114 } |
| paddy@87 | 115 return authCode.Scope, authCode.ProfileID, true |
| paddy@84 | 116 } |
| paddy@90 | 117 |
| paddy@90 | 118 func authCodeGrantInvalidate(r *http.Request, context Context) error { |
| paddy@90 | 119 // TODO(paddy): implement marking the authcode as used. |
| paddy@90 | 120 return nil |
| paddy@90 | 121 } |