auth

Paddy 2015-05-11 Parent:cf1aef6eb81f Child:b7e685839a1b

166:c45b946abe78 Go to Latest

auth/authcode.go

Implement a GetProfileHandler. Create a Handler that will allow us to return details about a Profile. Right now, you only get a single Profile at a time, which is problematic, because it will lead to N+1 requests. But we have no reason to retrieve anyone _else_'s Profile, so it's not like you need to be fetching any Profile other than your own. Also, this requires a Token issued for the Profile in question, which means you're limited to one Profile per request, anyways. Future avenues for exploration may be an admin Token granting access to many Profiles, the unspecified service flow for accessing the API, or simply accepting that name, join date, last active date, and ID are "public information".

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 }