auth

Paddy 2015-04-19 Parent:2809016184f6 Child:cf1aef6eb81f

163:73e12d5a1124 Go to Latest

auth/authcode.go

Use postgres arrays for scope associations. Use the new pqarrays library I wrote to store Scope associations for Tokens and AuthorizationCodes, instead of using our hacky and abstraction-breaking many-to-many code. We also created the authStore.deleteAuthorizationCodesByProfileID method, to clear out the AuthorizationCodes that belong to a Profile (used when the Profile is deleted). So we added the implementation for memstore and for our postgres store. Call Context.DeleteAuthorizationCodesByProfileID when deleting a Profile to clean up after it. Rename sortedScopes to Scopes, which we use pqarrays.StringArray's methods on to fulfill the sql.Scanner and driver.Valuer interfaces. This lets us store Scopes in postgres arrays. Create a stringsToScopes helper function that creates Scope objects, with their IDs filled by the strings specified. Update our GrantType.Validate function signature to return Scopes instead of []string. Create a Scopes.Strings() helper method that returns a []string of the IDs of the Scopes. Update our SQL init file to use the new postgres array definition, instead of the many-to-many definition.

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