auth

Paddy 2014-12-13 Parent:61a802849b51 Child:e81407f7ecb1

91:c1b3a36af1a7 Go to Latest

auth/token.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.

History
1 package auth
3 import (
4 "errors"
5 "time"
7 "code.secondbit.org/uuid"
8 )
10 const (
11 defaultTokenExpiration = 3600 // one hour
12 defaultRefreshTokenExpiration = 86400 // one day
13 )
15 var (
16 // ErrNoTokenStore is returned when a Context tries to act on a tokenStore without setting one first.
17 ErrNoTokenStore = errors.New("no tokenStore was specified for the Context")
18 // ErrTokenNotFound is returned when a Token is requested but not found in a tokenStore.
19 ErrTokenNotFound = errors.New("token not found in tokenStore")
20 // ErrTokenAlreadyExists is returned when a Token is added to a tokenStore, but another Token with
21 // the same AccessToken property already exists in the tokenStore.
22 ErrTokenAlreadyExists = errors.New("token already exists in tokenStore")
23 )
25 // Token represents an access and/or refresh token that the Client can use to access user data
26 // or obtain a new access token.
27 type Token struct {
28 AccessToken string
29 RefreshToken string
30 Created time.Time
31 CreatedFrom string
32 ExpiresIn int32
33 RefreshExpiresIn int32
34 TokenType string
35 Scope string
36 ProfileID uuid.ID
37 Revoked bool
38 }
40 type tokenStore interface {
41 // BUG(paddy): need to be able to revoke tokens and refresh tokens
42 getToken(token string, refresh bool) (Token, error)
43 saveToken(token Token) error
44 removeToken(token string) error
45 revokeToken(token string, refresh bool) error
46 getTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error)
47 }
49 func (m *memstore) getToken(token string, refresh bool) (Token, error) {
50 if refresh {
51 t, err := m.lookupTokenByRefresh(token)
52 if err != nil {
53 return Token{}, err
54 }
55 token = t
56 }
57 m.tokenLock.RLock()
58 defer m.tokenLock.RUnlock()
59 result, ok := m.tokens[token]
60 if !ok {
61 return Token{}, ErrTokenNotFound
62 }
63 return result, nil
64 }
66 func (m *memstore) saveToken(token Token) error {
67 m.tokenLock.Lock()
68 defer m.tokenLock.Unlock()
69 _, ok := m.tokens[token.AccessToken]
70 if ok {
71 return ErrTokenAlreadyExists
72 }
73 m.tokens[token.AccessToken] = token
74 if token.RefreshToken != "" {
75 m.refreshTokenLookup[token.RefreshToken] = token.AccessToken
76 }
77 if _, ok = m.profileTokenLookup[token.ProfileID.String()]; ok {
78 m.profileTokenLookup[token.ProfileID.String()] = append(m.profileTokenLookup[token.ProfileID.String()], token.AccessToken)
79 } else {
80 m.profileTokenLookup[token.ProfileID.String()] = []string{token.AccessToken}
81 }
82 return nil
83 }
85 func (m *memstore) removeToken(token string) error {
86 m.tokenLock.Lock()
87 defer m.tokenLock.Unlock()
88 t, ok := m.tokens[token]
89 if !ok {
90 return ErrTokenNotFound
91 }
92 delete(m.tokens, token)
93 if t.RefreshToken != "" {
94 delete(m.refreshTokenLookup, t.RefreshToken)
95 }
96 pos := -1
97 for p, item := range m.profileTokenLookup[t.ProfileID.String()] {
98 if item == token {
99 pos = p
100 break
101 }
102 }
103 if pos >= 0 {
104 m.profileTokenLookup[t.ProfileID.String()] = append(m.profileTokenLookup[t.ProfileID.String()][:pos], m.profileTokenLookup[t.ProfileID.String()][pos+1:]...)
105 }
106 return nil
107 }
109 func (m *memstore) revokeToken(token string, refresh bool) error {
110 if refresh {
111 t, err := m.lookupTokenByRefresh(token)
112 if err != nil {
113 return err
114 }
115 token = t
116 }
117 m.tokenLock.Lock()
118 defer m.tokenLock.Unlock()
119 t, ok := m.tokens[token]
120 if !ok {
121 return ErrTokenNotFound
122 }
123 t.Revoked = true
124 m.tokens[token] = t
125 return nil
126 }
128 func (m *memstore) getTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
129 ids, err := m.lookupTokensByProfileID(profileID.String())
130 if err != nil {
131 return []Token{}, err
132 }
133 if len(ids) > num+offset {
134 ids = ids[offset : num+offset]
135 } else if len(ids) > offset {
136 ids = ids[offset:]
137 } else {
138 return []Token{}, nil
139 }
140 tokens := []Token{}
141 for _, id := range ids {
142 token, err := m.getToken(id, false)
143 if err != nil {
144 return []Token{}, err
145 }
146 tokens = append(tokens, token)
147 }
148 return tokens, nil
149 }