auth

Paddy 2014-12-14 Parent:e81407f7ecb1 Child:c03b5eb3179e

99:5bccbed6631b Go to Latest

auth/token.go

Add an endpoint to validate and register profiles. Add a newProfileRequest object that defines the user-specified properties of a new Profile. Add a helper that validates a newProfileRequest and modifies it for sanitization, mostly just removing leading and trailing whitespace. Add MaxNameLength, MaxUsernameLength, and MaxEmailLength constants to hold the maximum length for those properties. Add errors to be returned when a users attempts to log in with a profile that is compromised or locked. Add the bare bones of a CreateProfileHandler that validates a profile registration request adn uses it to create a Profile and at least one Login. Create a requestError struct that is used for returning API errors, along with constants for the slugs we'll use to signal those errors.

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