auth

Paddy 2015-01-18 Parent:c03b5eb3179e Child:d14f0a81498c

123:0a1e16b9c141 Go to Latest

auth/authcode.go

Refactor verifyClient, implement refresh tokens. Refactor verifyClient into verifyClient and getClientAuth. We moved verifyClient out of each of the GrantType's validation functions and into the access token endpoint, where it will be called before the GrantType's validation function. Yay, less code repetition. And seeing as we always want to verify the client, that seems like a good way to prevent things like 118a69954621 from happening. This did, however, force us to add an AllowsPublic property to the GrantType, so the token endpoint knows whether or not a public Client is valid for any given GrantType. We also implemented the refresh token grant type, which required adding ClientID and RefreshRevoked as properties on the Token type. We need ClientID because we need to constrain refresh tokens to the client that issued them. We also should probably keep track of which tokens belong to which clients, just as a general rule of thumb. RefreshRevoked had to be created, next to Revoked, because the AccessToken could be revoked and the RefreshToken still valid, or vice versa. Notably, when you issue a new refresh token, the old one is revoked, but the access token is still valid. It remains to be seen whether this is a good way to track things or not. The number of duplicated properties lead me to believe our type is not a great representation of the underlying concepts.

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