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.
7 "code.secondbit.org/uuid"
11 defaultTokenExpiration = 3600 // one hour
12 defaultRefreshTokenExpiration = 86400 // one day
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")
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.
33 RefreshExpiresIn int32
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)
48 func (m *memstore) getToken(token string, refresh bool) (Token, error) {
50 t, err := m.lookupTokenByRefresh(token)
57 defer m.tokenLock.RUnlock()
58 result, ok := m.tokens[token]
60 return Token{}, ErrTokenNotFound
65 func (m *memstore) saveToken(token Token) error {
67 defer m.tokenLock.Unlock()
68 _, ok := m.tokens[token.AccessToken]
70 return ErrTokenAlreadyExists
72 m.tokens[token.AccessToken] = token
73 if token.RefreshToken != "" {
74 m.refreshTokenLookup[token.RefreshToken] = token.AccessToken
76 if _, ok = m.profileTokenLookup[token.ProfileID.String()]; ok {
77 m.profileTokenLookup[token.ProfileID.String()] = append(m.profileTokenLookup[token.ProfileID.String()], token.AccessToken)
79 m.profileTokenLookup[token.ProfileID.String()] = []string{token.AccessToken}
84 func (m *memstore) removeToken(token string) error {
86 defer m.tokenLock.Unlock()
87 t, ok := m.tokens[token]
89 return ErrTokenNotFound
91 delete(m.tokens, token)
92 if t.RefreshToken != "" {
93 delete(m.refreshTokenLookup, t.RefreshToken)
96 for p, item := range m.profileTokenLookup[t.ProfileID.String()] {
103 m.profileTokenLookup[t.ProfileID.String()] = append(m.profileTokenLookup[t.ProfileID.String()][:pos], m.profileTokenLookup[t.ProfileID.String()][pos+1:]...)
108 func (m *memstore) revokeToken(token string, refresh bool) error {
110 t, err := m.lookupTokenByRefresh(token)
117 defer m.tokenLock.Unlock()
118 t, ok := m.tokens[token]
120 return ErrTokenNotFound
127 func (m *memstore) getTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
128 ids, err := m.lookupTokensByProfileID(profileID.String())
130 return []Token{}, err
132 if len(ids) > num+offset {
133 ids = ids[offset : num+offset]
134 } else if len(ids) > offset {
137 return []Token{}, nil
140 for _, id := range ids {
141 token, err := m.getToken(id, false)
143 return []Token{}, err
145 tokens = append(tokens, token)