auth
auth/token.go
Correctly set Content-Type header when obtaining a token. Some OAuth2 clients break if you don't correctly set the Content-Type header when obtaining a token. Also, let's just try to be good Content-Type/Accept citizens, all around.
1 package auth
3 import (
4 "errors"
5 "time"
7 "code.secondbit.org/uuid.hg"
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 getToken(token string, refresh bool) (Token, error)
42 saveToken(token Token) error
43 removeToken(token string) error
44 revokeToken(token string, refresh bool) error
45 getTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error)
46 }
48 func (m *memstore) getToken(token string, refresh bool) (Token, error) {
49 if refresh {
50 t, err := m.lookupTokenByRefresh(token)
51 if err != nil {
52 return Token{}, err
53 }
54 token = t
55 }
56 m.tokenLock.RLock()
57 defer m.tokenLock.RUnlock()
58 result, ok := m.tokens[token]
59 if !ok {
60 return Token{}, ErrTokenNotFound
61 }
62 return result, nil
63 }
65 func (m *memstore) saveToken(token Token) error {
66 m.tokenLock.Lock()
67 defer m.tokenLock.Unlock()
68 _, ok := m.tokens[token.AccessToken]
69 if ok {
70 return ErrTokenAlreadyExists
71 }
72 m.tokens[token.AccessToken] = token
73 if token.RefreshToken != "" {
74 m.refreshTokenLookup[token.RefreshToken] = token.AccessToken
75 }
76 if _, ok = m.profileTokenLookup[token.ProfileID.String()]; ok {
77 m.profileTokenLookup[token.ProfileID.String()] = append(m.profileTokenLookup[token.ProfileID.String()], token.AccessToken)
78 } else {
79 m.profileTokenLookup[token.ProfileID.String()] = []string{token.AccessToken}
80 }
81 return nil
82 }
84 func (m *memstore) removeToken(token string) error {
85 m.tokenLock.Lock()
86 defer m.tokenLock.Unlock()
87 t, ok := m.tokens[token]
88 if !ok {
89 return ErrTokenNotFound
90 }
91 delete(m.tokens, token)
92 if t.RefreshToken != "" {
93 delete(m.refreshTokenLookup, t.RefreshToken)
94 }
95 pos := -1
96 for p, item := range m.profileTokenLookup[t.ProfileID.String()] {
97 if item == token {
98 pos = p
99 break
100 }
101 }
102 if pos >= 0 {
103 m.profileTokenLookup[t.ProfileID.String()] = append(m.profileTokenLookup[t.ProfileID.String()][:pos], m.profileTokenLookup[t.ProfileID.String()][pos+1:]...)
104 }
105 return nil
106 }
108 func (m *memstore) revokeToken(token string, refresh bool) error {
109 if refresh {
110 t, err := m.lookupTokenByRefresh(token)
111 if err != nil {
112 return err
113 }
114 token = t
115 }
116 m.tokenLock.Lock()
117 defer m.tokenLock.Unlock()
118 t, ok := m.tokens[token]
119 if !ok {
120 return ErrTokenNotFound
121 }
122 t.Revoked = true
123 m.tokens[token] = t
124 return nil
125 }
127 func (m *memstore) getTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
128 ids, err := m.lookupTokensByProfileID(profileID.String())
129 if err != nil {
130 return []Token{}, err
131 }
132 if len(ids) > num+offset {
133 ids = ids[offset : num+offset]
134 } else if len(ids) > offset {
135 ids = ids[offset:]
136 } else {
137 return []Token{}, nil
138 }
139 tokens := []Token{}
140 for _, id := range ids {
141 token, err := m.getToken(id, false)
142 if err != nil {
143 return []Token{}, err
144 }
145 tokens = append(tokens, token)
146 }
147 return tokens, nil
148 }