storage -> Storage
This commit is contained in:
parent
68daa3ed74
commit
0137ea5e68
3 changed files with 35 additions and 29 deletions
|
@ -32,6 +32,9 @@ type Storage interface {
|
|||
CheckUsernamePassword(username, password, id string) error
|
||||
}
|
||||
|
||||
// SetupServer creates an OIDC server with Issuer=http://localhost:<port>
|
||||
//
|
||||
// Use one of the pre-made clients in storage/clients.go or register a new one.
|
||||
func SetupServer(ctx context.Context, port string, storage Storage) *mux.Router {
|
||||
// this will allow us to use an issuer with http:// instead of https://
|
||||
os.Setenv(op.OidcDevMode, "true")
|
||||
|
|
|
@ -128,6 +128,9 @@ func (c *Client) ClockSkew() time.Duration {
|
|||
// RegisterClients enables you to register clients for the example implementation
|
||||
// there are some clients (web and native) to try out different cases
|
||||
// add more if necessary
|
||||
//
|
||||
// RegisterClients should be called before the Storage is used so that there are
|
||||
// no race conditions.
|
||||
func RegisterClients(registerClients ...*Client) {
|
||||
for _, client := range registerClients {
|
||||
clients[client.id] = client
|
||||
|
|
|
@ -32,7 +32,7 @@ var serviceKey1 = &rsa.PublicKey{
|
|||
// storage implements the op.Storage interface
|
||||
// typically you would implement this as a layer on top of your database
|
||||
// for simplicity this example keeps everything in-memory
|
||||
type storage struct {
|
||||
type Storage struct {
|
||||
lock sync.Mutex
|
||||
authRequests map[string]*AuthRequest
|
||||
codes map[string]string
|
||||
|
@ -50,9 +50,9 @@ type signingKey struct {
|
|||
Key *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func NewStorage(userStore UserStore) *storage {
|
||||
func NewStorage(userStore UserStore) *Storage {
|
||||
key, _ := rsa.GenerateKey(rand.Reader, 2048)
|
||||
return &storage{
|
||||
return &Storage{
|
||||
authRequests: make(map[string]*AuthRequest),
|
||||
codes: make(map[string]string),
|
||||
tokens: make(map[string]*Token),
|
||||
|
@ -75,7 +75,7 @@ func NewStorage(userStore UserStore) *storage {
|
|||
}
|
||||
|
||||
// CheckUsernamePassword implements the `authenticate` interface of the login
|
||||
func (s *storage) CheckUsernamePassword(username, password, id string) error {
|
||||
func (s *Storage) CheckUsernamePassword(username, password, id string) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
request, ok := s.authRequests[id]
|
||||
|
@ -103,7 +103,7 @@ func (s *storage) CheckUsernamePassword(username, password, id string) error {
|
|||
|
||||
// CreateAuthRequest implements the op.Storage interface
|
||||
// it will be called after parsing and validation of the authentication request
|
||||
func (s *storage) CreateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, userID string) (op.AuthRequest, error) {
|
||||
func (s *Storage) CreateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, userID string) (op.AuthRequest, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
|
@ -122,7 +122,7 @@ func (s *storage) CreateAuthRequest(ctx context.Context, authReq *oidc.AuthReque
|
|||
|
||||
// AuthRequestByID implements the op.Storage interface
|
||||
// it will be called after the Login UI redirects back to the OIDC endpoint
|
||||
func (s *storage) AuthRequestByID(ctx context.Context, id string) (op.AuthRequest, error) {
|
||||
func (s *Storage) AuthRequestByID(ctx context.Context, id string) (op.AuthRequest, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
request, ok := s.authRequests[id]
|
||||
|
@ -134,7 +134,7 @@ func (s *storage) AuthRequestByID(ctx context.Context, id string) (op.AuthReques
|
|||
|
||||
// AuthRequestByCode implements the op.Storage interface
|
||||
// it will be called after parsing and validation of the token request (in an authorization code flow)
|
||||
func (s *storage) AuthRequestByCode(ctx context.Context, code string) (op.AuthRequest, error) {
|
||||
func (s *Storage) AuthRequestByCode(ctx context.Context, code string) (op.AuthRequest, error) {
|
||||
// for this example we read the id by code and then get the request by id
|
||||
requestID, ok := func() (string, bool) {
|
||||
s.lock.Lock()
|
||||
|
@ -151,7 +151,7 @@ func (s *storage) AuthRequestByCode(ctx context.Context, code string) (op.AuthRe
|
|||
// SaveAuthCode implements the op.Storage interface
|
||||
// it will be called after the authentication has been successful and before redirecting the user agent to the redirect_uri
|
||||
//(in an authorization code flow)
|
||||
func (s *storage) SaveAuthCode(ctx context.Context, id string, code string) error {
|
||||
func (s *Storage) SaveAuthCode(ctx context.Context, id string, code string) error {
|
||||
// for this example we'll just save the authRequestID to the code
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
@ -163,7 +163,7 @@ func (s *storage) SaveAuthCode(ctx context.Context, id string, code string) erro
|
|||
// it will be called after creating the token response (id and access tokens) for a valid
|
||||
//- authentication request (in an implicit flow)
|
||||
//- token request (in an authorization code flow)
|
||||
func (s *storage) DeleteAuthRequest(ctx context.Context, id string) error {
|
||||
func (s *Storage) DeleteAuthRequest(ctx context.Context, id string) error {
|
||||
// you can simply delete all reference to the auth request
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
@ -179,7 +179,7 @@ func (s *storage) DeleteAuthRequest(ctx context.Context, id string) error {
|
|||
|
||||
// CreateAccessToken implements the op.Storage interface
|
||||
// it will be called for all requests able to return an access token (Authorization Code Flow, Implicit Flow, JWT Profile, ...)
|
||||
func (s *storage) CreateAccessToken(ctx context.Context, request op.TokenRequest) (string, time.Time, error) {
|
||||
func (s *Storage) CreateAccessToken(ctx context.Context, request op.TokenRequest) (string, time.Time, error) {
|
||||
var applicationID string
|
||||
// if authenticated for an app (auth code / implicit flow) we must save the client_id to the token
|
||||
authReq, ok := request.(*AuthRequest)
|
||||
|
@ -195,7 +195,7 @@ func (s *storage) CreateAccessToken(ctx context.Context, request op.TokenRequest
|
|||
|
||||
// CreateAccessAndRefreshTokens implements the op.Storage interface
|
||||
// it will be called for all requests able to return an access and refresh token (Authorization Code Flow, Refresh Token Request)
|
||||
func (s *storage) CreateAccessAndRefreshTokens(ctx context.Context, request op.TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshToken string, expiration time.Time, err error) {
|
||||
func (s *Storage) CreateAccessAndRefreshTokens(ctx context.Context, request op.TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshToken string, expiration time.Time, err error) {
|
||||
// get the information depending on the request type / implementation
|
||||
applicationID, authTime, amr := getInfoFromRequest(request)
|
||||
|
||||
|
@ -228,7 +228,7 @@ func (s *storage) CreateAccessAndRefreshTokens(ctx context.Context, request op.T
|
|||
|
||||
// TokenRequestByRefreshToken implements the op.Storage interface
|
||||
// it will be called after parsing and validation of the refresh token request
|
||||
func (s *storage) TokenRequestByRefreshToken(ctx context.Context, refreshToken string) (op.RefreshTokenRequest, error) {
|
||||
func (s *Storage) TokenRequestByRefreshToken(ctx context.Context, refreshToken string) (op.RefreshTokenRequest, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
token, ok := s.refreshTokens[refreshToken]
|
||||
|
@ -240,7 +240,7 @@ func (s *storage) TokenRequestByRefreshToken(ctx context.Context, refreshToken s
|
|||
|
||||
// TerminateSession implements the op.Storage interface
|
||||
// it will be called after the user signed out, therefore the access and refresh token of the user of this client must be removed
|
||||
func (s *storage) TerminateSession(ctx context.Context, userID string, clientID string) error {
|
||||
func (s *Storage) TerminateSession(ctx context.Context, userID string, clientID string) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
for _, token := range s.tokens {
|
||||
|
@ -255,7 +255,7 @@ func (s *storage) TerminateSession(ctx context.Context, userID string, clientID
|
|||
|
||||
// RevokeToken implements the op.Storage interface
|
||||
// it will be called after parsing and validation of the token revocation request
|
||||
func (s *storage) RevokeToken(ctx context.Context, token string, userID string, clientID string) *oidc.Error {
|
||||
func (s *Storage) RevokeToken(ctx context.Context, token string, userID string, clientID string) *oidc.Error {
|
||||
// a single token was requested to be removed
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
@ -291,7 +291,7 @@ func (s *storage) RevokeToken(ctx context.Context, token string, userID string,
|
|||
|
||||
// GetSigningKey implements the op.Storage interface
|
||||
// it will be called when creating the OpenID Provider
|
||||
func (s *storage) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey) {
|
||||
func (s *Storage) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKey) {
|
||||
// in this example the signing key is a static rsa.PrivateKey and the algorithm used is RS256
|
||||
// you would obviously have a more complex implementation and store / retrieve the key from your database as well
|
||||
//
|
||||
|
@ -308,7 +308,7 @@ func (s *storage) GetSigningKey(ctx context.Context, keyCh chan<- jose.SigningKe
|
|||
|
||||
// GetKeySet implements the op.Storage interface
|
||||
// it will be called to get the current (public) keys, among others for the keys_endpoint or for validating access_tokens on the userinfo_endpoint, ...
|
||||
func (s *storage) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error) {
|
||||
func (s *Storage) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error) {
|
||||
// as mentioned above, this example only has a single signing key without key rotation,
|
||||
// so it will directly use its public key
|
||||
//
|
||||
|
@ -328,7 +328,7 @@ func (s *storage) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error) {
|
|||
|
||||
// GetClientByClientID implements the op.Storage interface
|
||||
// it will be called whenever information (type, redirect_uris, ...) about the client behind the client_id is needed
|
||||
func (s *storage) GetClientByClientID(ctx context.Context, clientID string) (op.Client, error) {
|
||||
func (s *Storage) GetClientByClientID(ctx context.Context, clientID string) (op.Client, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
client, ok := s.clients[clientID]
|
||||
|
@ -340,7 +340,7 @@ func (s *storage) GetClientByClientID(ctx context.Context, clientID string) (op.
|
|||
|
||||
// AuthorizeClientIDSecret implements the op.Storage interface
|
||||
// it will be called for validating the client_id, client_secret on token or introspection requests
|
||||
func (s *storage) AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error {
|
||||
func (s *Storage) AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
client, ok := s.clients[clientID]
|
||||
|
@ -357,13 +357,13 @@ func (s *storage) AuthorizeClientIDSecret(ctx context.Context, clientID, clientS
|
|||
|
||||
// SetUserinfoFromScopes implements the op.Storage interface
|
||||
// it will be called for the creation of an id_token, so we'll just pass it to the private function without any further check
|
||||
func (s *storage) SetUserinfoFromScopes(ctx context.Context, userinfo oidc.UserInfoSetter, userID, clientID string, scopes []string) error {
|
||||
func (s *Storage) SetUserinfoFromScopes(ctx context.Context, userinfo oidc.UserInfoSetter, userID, clientID string, scopes []string) error {
|
||||
return s.setUserinfo(ctx, userinfo, userID, clientID, scopes)
|
||||
}
|
||||
|
||||
// SetUserinfoFromToken implements the op.Storage interface
|
||||
// it will be called for the userinfo endpoint, so we read the token and pass the information from that to the private function
|
||||
func (s *storage) SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error {
|
||||
func (s *Storage) SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error {
|
||||
token, ok := func() (*Token, bool) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
@ -391,7 +391,7 @@ func (s *storage) SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserIn
|
|||
|
||||
// SetIntrospectionFromToken implements the op.Storage interface
|
||||
// it will be called for the introspection endpoint, so we read the token and pass the information from that to the private function
|
||||
func (s *storage) SetIntrospectionFromToken(ctx context.Context, introspection oidc.IntrospectionResponse, tokenID, subject, clientID string) error {
|
||||
func (s *Storage) SetIntrospectionFromToken(ctx context.Context, introspection oidc.IntrospectionResponse, tokenID, subject, clientID string) error {
|
||||
token, ok := func() (*Token, bool) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
@ -424,7 +424,7 @@ func (s *storage) SetIntrospectionFromToken(ctx context.Context, introspection o
|
|||
|
||||
// GetPrivateClaimsFromScopes implements the op.Storage interface
|
||||
// it will be called for the creation of a JWT access token to assert claims for custom scopes
|
||||
func (s *storage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (claims map[string]interface{}, err error) {
|
||||
func (s *Storage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (claims map[string]interface{}, err error) {
|
||||
for _, scope := range scopes {
|
||||
switch scope {
|
||||
case CustomScope:
|
||||
|
@ -436,7 +436,7 @@ func (s *storage) GetPrivateClaimsFromScopes(ctx context.Context, userID, client
|
|||
|
||||
// GetKeyByIDAndUserID implements the op.Storage interface
|
||||
// it will be called to validate the signatures of a JWT (JWT Profile Grant and Authentication)
|
||||
func (s *storage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error) {
|
||||
func (s *Storage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
service, ok := s.services[userID]
|
||||
|
@ -456,7 +456,7 @@ func (s *storage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string)
|
|||
|
||||
// ValidateJWTProfileScopes implements the op.Storage interface
|
||||
// it will be called to validate the scopes of a JWT Profile Authorization Grant request
|
||||
func (s *storage) ValidateJWTProfileScopes(ctx context.Context, userID string, scopes []string) ([]string, error) {
|
||||
func (s *Storage) ValidateJWTProfileScopes(ctx context.Context, userID string, scopes []string) ([]string, error) {
|
||||
allowedScopes := make([]string, 0)
|
||||
for _, scope := range scopes {
|
||||
if scope == oidc.ScopeOpenID {
|
||||
|
@ -467,12 +467,12 @@ func (s *storage) ValidateJWTProfileScopes(ctx context.Context, userID string, s
|
|||
}
|
||||
|
||||
// Health implements the op.Storage interface
|
||||
func (s *storage) Health(ctx context.Context) error {
|
||||
func (s *Storage) Health(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// createRefreshToken will store a refresh_token in-memory based on the provided information
|
||||
func (s *storage) createRefreshToken(accessToken *Token, amr []string, authTime time.Time) (string, error) {
|
||||
func (s *Storage) createRefreshToken(accessToken *Token, amr []string, authTime time.Time) (string, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
token := &RefreshToken{
|
||||
|
@ -491,7 +491,7 @@ func (s *storage) createRefreshToken(accessToken *Token, amr []string, authTime
|
|||
}
|
||||
|
||||
// renewRefreshToken checks the provided refresh_token and creates a new one based on the current
|
||||
func (s *storage) renewRefreshToken(currentRefreshToken string) (string, string, error) {
|
||||
func (s *Storage) renewRefreshToken(currentRefreshToken string) (string, string, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
refreshToken, ok := s.refreshTokens[currentRefreshToken]
|
||||
|
@ -514,7 +514,7 @@ func (s *storage) renewRefreshToken(currentRefreshToken string) (string, string,
|
|||
}
|
||||
|
||||
// accessToken will store an access_token in-memory based on the provided information
|
||||
func (s *storage) accessToken(applicationID, refreshTokenID, subject string, audience, scopes []string) (*Token, error) {
|
||||
func (s *Storage) accessToken(applicationID, refreshTokenID, subject string, audience, scopes []string) (*Token, error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
token := &Token{
|
||||
|
@ -531,7 +531,7 @@ func (s *storage) accessToken(applicationID, refreshTokenID, subject string, aud
|
|||
}
|
||||
|
||||
// setUserinfo sets the info based on the user, scopes and if necessary the clientID
|
||||
func (s *storage) setUserinfo(ctx context.Context, userInfo oidc.UserInfoSetter, userID, clientID string, scopes []string) (err error) {
|
||||
func (s *Storage) setUserinfo(ctx context.Context, userInfo oidc.UserInfoSetter, userID, clientID string, scopes []string) (err error) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
user := s.userStore.GetUserByID(userID)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue