fix(oidc): subject generated for anonymous users (#3238)

Fix and issue that would prevent a correct ID Token from being generated for users who start off anonymous. This also avoids generating one in the first place for anonymous users.
This commit is contained in:
James Elliott 2022-04-25 10:31:05 +10:00 committed by GitHub
parent 038ec1d2cf
commit abf1c86ab9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 435 additions and 44 deletions

View File

@ -23,7 +23,7 @@ func OAuthIntrospectionPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter
if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewIntrospectionRequest(ctx, req, oidcSession); err != nil { if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewIntrospectionRequest(ctx, req, oidcSession); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Introspection Request failed with error: %s", rfc.GetDescription()) ctx.Logger.Errorf("Introspection Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.Fosite.WriteIntrospectionError(rw, err) ctx.Providers.OpenIDConnect.Fosite.WriteIntrospectionError(rw, err)

View File

@ -17,7 +17,7 @@ func OAuthRevocationPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter, r
if err = ctx.Providers.OpenIDConnect.Fosite.NewRevocationRequest(ctx, req); err != nil { if err = ctx.Providers.OpenIDConnect.Fosite.NewRevocationRequest(ctx, req); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Revocation Request failed with error: %s", rfc.GetDescription()) ctx.Logger.Errorf("Revocation Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
} }
ctx.Providers.OpenIDConnect.Fosite.WriteRevocationResponse(rw, err) ctx.Providers.OpenIDConnect.Fosite.WriteRevocationResponse(rw, err)

View File

@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/google/uuid"
"github.com/ory/fosite" "github.com/ory/fosite"
"github.com/authelia/authelia/v4/internal/middlewares" "github.com/authelia/authelia/v4/internal/middlewares"
@ -29,7 +28,7 @@ func OpenIDConnectAuthorizationGET(ctx *middlewares.AutheliaCtx, rw http.Respons
if requester, err = ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeRequest(ctx, r); err != nil { if requester, err = ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeRequest(ctx, r); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Authorization Request failed with error: %s", rfc.GetDescription()) ctx.Logger.Errorf("Authorization Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, err) ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, err)
@ -62,14 +61,18 @@ func OpenIDConnectAuthorizationGET(ctx *middlewares.AutheliaCtx, rw http.Respons
userSession := ctx.GetSession() userSession := ctx.GetSession()
var subject uuid.UUID var subject model.NullUUID
if subject, err = ctx.Providers.OpenIDConnect.Store.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil { if userSession.Username != "" {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred retrieving subject for user '%s': %+v", requester.GetID(), client.GetID(), userSession.Username, err) if subject.UUID, err = ctx.Providers.OpenIDConnect.Store.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred retrieving subject for user '%s': %+v", requester.GetID(), client.GetID(), userSession.Username, err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not retrieve the subject.")) ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not retrieve the subject."))
return return
}
subject.Valid = true
} }
var ( var (
@ -104,7 +107,7 @@ func OpenIDConnectAuthorizationGET(ctx *middlewares.AutheliaCtx, rw http.Respons
if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeResponse(ctx, requester, oidcSession); err != nil { if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewAuthorizeResponse(ctx, requester, oidcSession); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Authorization Response for Request with id '%s' on client with id '%s' could not be created: %s", requester.GetID(), clientID, rfc.GetDescription()) ctx.Logger.Errorf("Authorization Response for Request with id '%s' on client with id '%s' could not be created: %s", requester.GetID(), clientID, rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, err) ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, err)

View File

@ -17,12 +17,18 @@ import (
) )
func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, rootURI string, client *oidc.Client, func handleOIDCAuthorizationConsent(ctx *middlewares.AutheliaCtx, rootURI string, client *oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject model.NullUUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
if userSession.ConsentChallengeID != nil { if userSession.ConsentChallengeID != nil {
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' proceeding to lookup consent by challenge id '%s'", requester.GetID(), client.GetID(), userSession.ConsentChallengeID)
return handleOIDCAuthorizationConsentWithChallengeID(ctx, rootURI, client, userSession, rw, r, requester) return handleOIDCAuthorizationConsentWithChallengeID(ctx, rootURI, client, userSession, rw, r, requester)
} }
if !subject.Valid {
return handleOIDCAuthorizationConsentGenerate(ctx, rootURI, client, userSession, subject, rw, r, requester)
}
return handleOIDCAuthorizationConsentOrGenerate(ctx, rootURI, client, userSession, subject, rw, r, requester) return handleOIDCAuthorizationConsentOrGenerate(ctx, rootURI, client, userSession, subject, rw, r, requester)
} }
@ -47,6 +53,26 @@ func handleOIDCAuthorizationConsentWithChallengeID(ctx *middlewares.AutheliaCtx,
return nil, true return nil, true
} }
if !consent.Subject.Valid {
if consent.Subject.UUID, err = ctx.Providers.OpenIDConnect.Store.GetSubject(ctx, client.GetSectorIdentifier(), userSession.Username); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred retrieving subject for user '%s': %+v", requester.GetID(), client.GetID(), userSession.Username, err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not retrieve the subject."))
return nil, true
}
consent.Subject.Valid = true
if err = ctx.Providers.StorageProvider.SaveOAuth2ConsentSessionSubject(ctx, *consent); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred updating consent session subject for user '%s': %+v", requester.GetID(), client.GetID(), userSession.Username, err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not update the consent session subject."))
return nil, true
}
}
if consent.Responded() { if consent.Responded() {
userSession.ConsentChallengeID = nil userSession.ConsentChallengeID = nil
@ -85,40 +111,38 @@ func handleOIDCAuthorizationConsentWithChallengeID(ctx *middlewares.AutheliaCtx,
} }
func handleOIDCAuthorizationConsentOrGenerate(ctx *middlewares.AutheliaCtx, rootURI string, client *oidc.Client, func handleOIDCAuthorizationConsentOrGenerate(ctx *middlewares.AutheliaCtx, rootURI string, client *oidc.Client,
userSession session.UserSession, subject uuid.UUID, userSession session.UserSession, subject model.NullUUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) { rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var ( var (
rows *storage.ConsentSessionRows err error
scopes, audience []string
err error
) )
if rows, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionsPreConfigured(ctx, client.GetID(), subject); err != nil { scopes, audience := getExpectedScopesAndAudience(requester)
if consent, err = getOIDCPreconfiguredConsent(ctx, client.GetID(), subject.UUID, scopes, audience); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' had error looking up pre-configured consent sessions: %+v", requester.GetID(), requester.GetClient().GetID(), err) ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' had error looking up pre-configured consent sessions: %+v", requester.GetID(), requester.GetClient().GetID(), err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not lookup the consent session."))
return nil, true
} }
defer rows.Close() if consent != nil {
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' successfully looked up pre-configured consent with challenge id '%s'", requester.GetID(), client.GetID(), consent.ChallengeID)
for rows.Next() {
if consent, err = rows.Get(); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' had error looking up pre-configured consent sessions: %+v", requester.GetID(), requester.GetClient().GetID(), err)
ctx.Providers.OpenIDConnect.Fosite.WriteAuthorizeError(rw, requester, fosite.ErrServerError.WithHint("Could not lookup pre-configured consent sessions."))
return nil, true
}
scopes, audience = getExpectedScopesAndAudience(requester)
if consent.HasExactGrants(scopes, audience) && consent.CanGrant() {
break
}
}
if consent != nil && consent.HasExactGrants(scopes, audience) && consent.CanGrant() {
return consent, false return consent, false
} }
ctx.Logger.Debugf("Authorization Request with id '%s' on client with id '%s' proceeding to generate a new consent due to unsuccessful lookup of pre-configured consent", requester.GetID(), client.GetID())
return handleOIDCAuthorizationConsentGenerate(ctx, rootURI, client, userSession, subject, rw, r, requester)
}
func handleOIDCAuthorizationConsentGenerate(ctx *middlewares.AutheliaCtx, rootURI string, client *oidc.Client,
userSession session.UserSession, subject model.NullUUID,
rw http.ResponseWriter, r *http.Request, requester fosite.AuthorizeRequester) (consent *model.OAuth2ConsentSession, handled bool) {
var err error
if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil { if consent, err = model.NewOAuth2ConsentSession(subject, requester); err != nil {
ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred generating consent: %+v", requester.GetID(), requester.GetClient().GetID(), err) ctx.Logger.Errorf("Authorization Request with id '%s' on client with id '%s' could not be processed: error occurred generating consent: %+v", requester.GetID(), requester.GetClient().GetID(), err)
@ -166,3 +190,45 @@ func getExpectedScopesAndAudience(requester fosite.Requester) (scopes, audience
return requester.GetRequestedScopes(), audience return requester.GetRequestedScopes(), audience
} }
func getOIDCPreconfiguredConsent(ctx *middlewares.AutheliaCtx, clientID string, subject uuid.UUID, scopes, audience []string) (consent *model.OAuth2ConsentSession, err error) {
var (
rows *storage.ConsentSessionRows
)
ctx.Logger.Debugf("Consent Session is being checked for pre-configuration with signature of client id '%s' and subject '%s'", clientID, subject)
if rows, err = ctx.Providers.StorageProvider.LoadOAuth2ConsentSessionsPreConfigured(ctx, clientID, subject); err != nil {
ctx.Logger.Debugf("Consent Session checked for pre-configuration with signature of client id '%s' and subject '%s' failed with error during load: %+v", clientID, subject, err)
return nil, err
}
defer func() {
if err := rows.Close(); err != nil {
ctx.Logger.Errorf("Consent Session checked for pre-configuration with signature of client id '%s' and subject '%s' failed to close rows with error: %+v", clientID, subject, err)
}
}()
for rows.Next() {
if consent, err = rows.Get(); err != nil {
ctx.Logger.Debugf("Consent Session checked for pre-configuration with signature of client id '%s' and subject '%s' failed with error during iteration: %+v", clientID, subject, err)
return nil, err
}
if consent.HasExactGrants(scopes, audience) && consent.CanGrant() {
break
}
}
if consent != nil && consent.HasExactGrants(scopes, audience) && consent.CanGrant() {
ctx.Logger.Debugf("Consent Session checked for pre-configuration with signature of client id '%s' and subject '%s' found a result with challenge id '%s'", clientID, subject, consent.ChallengeID)
return consent, nil
}
ctx.Logger.Debugf("Consent Session checked for pre-configuration with signature of client id '%s' and subject '%s' did not find any results", clientID, subject)
return nil, nil
}

View File

@ -24,7 +24,7 @@ func OpenIDConnectTokenPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter
if requester, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessRequest(ctx, req, oidcSession); err != nil { if requester, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessRequest(ctx, req, oidcSession); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Access Request failed with error: %s", rfc.GetDescription()) ctx.Logger.Errorf("Access Request failed with error: %s", rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, requester, err) ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, requester, err)
@ -47,7 +47,7 @@ func OpenIDConnectTokenPOST(ctx *middlewares.AutheliaCtx, rw http.ResponseWriter
if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessResponse(ctx, requester); err != nil { if responder, err = ctx.Providers.OpenIDConnect.Fosite.NewAccessResponse(ctx, requester); err != nil {
rfc := fosite.ErrorToRFC6749Error(err) rfc := fosite.ErrorToRFC6749Error(err)
ctx.Logger.Errorf("Access Response for Request with id '%s' failed to be created with error: %s", requester.GetID(), rfc.GetDescription()) ctx.Logger.Errorf("Access Response for Request with id '%s' failed to be created with error: %s", requester.GetID(), rfc.WithExposeDebug(true).GetDescription())
ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, requester, err) ctx.Providers.OpenIDConnect.Fosite.WriteAccessError(rw, requester, err)

View File

@ -518,6 +518,20 @@ func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionResponse(arg0, arg1,
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionResponse", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionResponse), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionResponse", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionResponse), arg0, arg1, arg2)
} }
// SaveOAuth2ConsentSessionSubject mocks base method.
func (m *MockStorage) SaveOAuth2ConsentSessionSubject(arg0 context.Context, arg1 model.OAuth2ConsentSession) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SaveOAuth2ConsentSessionSubject", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// SaveOAuth2ConsentSessionSubject indicates an expected call of SaveOAuth2ConsentSessionSubject.
func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionSubject(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionSubject", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionSubject), arg0, arg1)
}
// SaveOAuth2Session mocks base method. // SaveOAuth2Session mocks base method.
func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 model.OAuth2Session) error { func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth2SessionType, arg2 model.OAuth2Session) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()

View File

@ -17,7 +17,7 @@ import (
) )
// NewOAuth2ConsentSession creates a new OAuth2ConsentSession. // NewOAuth2ConsentSession creates a new OAuth2ConsentSession.
func NewOAuth2ConsentSession(subject uuid.UUID, r fosite.Requester) (consent *OAuth2ConsentSession, err error) { func NewOAuth2ConsentSession(subject NullUUID, r fosite.Requester) (consent *OAuth2ConsentSession, err error) {
consent = &OAuth2ConsentSession{ consent = &OAuth2ConsentSession{
ClientID: r.GetClient().GetID(), ClientID: r.GetClient().GetID(),
Subject: subject, Subject: subject,
@ -86,7 +86,7 @@ type OAuth2ConsentSession struct {
ID int `db:"id"` ID int `db:"id"`
ChallengeID uuid.UUID `db:"challenge_id"` ChallengeID uuid.UUID `db:"challenge_id"`
ClientID string `db:"client_id"` ClientID string `db:"client_id"`
Subject uuid.UUID `db:"subject"` Subject NullUUID `db:"subject"`
Authorized bool `db:"authorized"` Authorized bool `db:"authorized"`
Granted bool `db:"granted"` Granted bool `db:"granted"`

View File

@ -7,9 +7,37 @@ import (
"fmt" "fmt"
"net" "net"
"github.com/google/uuid"
"github.com/authelia/authelia/v4/internal/utils" "github.com/authelia/authelia/v4/internal/utils"
) )
// NullUUID is a nullable uuid.UUID.
type NullUUID struct {
uuid.UUID
Valid bool
}
// Value is the NullUUID implementation of the databases/sql driver.Valuer.
func (u NullUUID) Value() (value driver.Value, err error) {
if !u.Valid {
return nil, nil
}
return u.UUID.Value()
}
// Scan is the NullUUID implementation of the sql.Scanner.
func (u *NullUUID) Scan(src interface{}) (err error) {
if src == nil {
u.UUID, u.Valid = uuid.UUID{}, false
return nil
}
return u.UUID.Scan(src)
}
// NewIP easily constructs a new IP. // NewIP easily constructs a new IP.
func NewIP(value net.IP) (ip IP) { func NewIP(value net.IP) (ip IP) {
return IP{IP: value} return IP{IP: value}

View File

@ -56,7 +56,7 @@ func TestNewSessionWithAuthorizeRequest(t *testing.T) {
consent := &model.OAuth2ConsentSession{ consent := &model.OAuth2ConsentSession{
ChallengeID: uuid.New(), ChallengeID: uuid.New(),
RequestedAt: requested, RequestedAt: requested,
Subject: subject, Subject: model.NullUUID{UUID: subject, Valid: true},
} }
session := NewSessionWithAuthorizeRequest(issuer, "primary", "john", amr, extra, authAt, consent, request) session := NewSessionWithAuthorizeRequest(issuer, "primary", "john", amr, extra, authAt, consent, request)

View File

@ -78,7 +78,7 @@ const (
const ( const (
// This is the latest schema version for the purpose of tests. // This is the latest schema version for the purpose of tests.
testLatestVersion = 4 testLatestVersion = 5
) )
const ( const (

View File

@ -0,0 +1,3 @@
ALTER TABLE oauth2_consent_session
DROP FOREIGN KEY oauth2_consent_session_subject_fkey,
ADD CONSTRAINT oauth2_consent_subject_fkey FOREIGN KEY (subject) REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT;

View File

@ -0,0 +1,4 @@
ALTER TABLE oauth2_consent_session MODIFY subject CHAR(36) NULL DEFAULT NULL;
ALTER TABLE oauth2_consent_session
DROP FOREIGN KEY oauth2_consent_subject_fkey,
ADD CONSTRAINT oauth2_consent_session_subject_fkey FOREIGN KEY (subject) REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT;

View File

@ -0,0 +1 @@
ALTER TABLE oauth2_consent_session RENAME CONSTRAINT oauth2_consent_session_subject_fkey TO oauth2_consent_subject_fkey;

View File

@ -0,0 +1,3 @@
ALTER TABLE oauth2_consent_session ALTER COLUMN subject DROP NOT NULL;
ALTER TABLE oauth2_consent_session ALTER COLUMN subject SET DEFAULT NULL;
ALTER TABLE oauth2_consent_session RENAME CONSTRAINT oauth2_consent_subject_fkey TO oauth2_consent_session_subject_fkey;

View File

@ -0,0 +1 @@
SELECT 1;

View File

@ -0,0 +1,251 @@
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE oauth2_consent_session RENAME TO _bkp_UP_V0005_oauth2_consent_session;
CREATE TABLE IF NOT EXISTS oauth2_consent_session (
id INTEGER,
challenge_id CHAR(36) NOT NULL,
client_id VARCHAR(255) NOT NULL,
subject CHAR(36) NULL DEFAULT NULL,
authorized BOOLEAN NOT NULL DEFAULT FALSE,
granted BOOLEAN NOT NULL DEFAULT FALSE,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
responded_at TIMESTAMP NULL DEFAULT NULL,
expires_at TIMESTAMP NULL DEFAULT NULL,
form_data TEXT NOT NULL,
requested_scopes TEXT NOT NULL,
granted_scopes TEXT NOT NULL,
requested_audience TEXT NULL DEFAULT '',
granted_audience TEXT NULL DEFAULT '',
PRIMARY KEY (id),
CONSTRAINT oauth2_consent_session_subject_fkey
FOREIGN KEY(subject)
REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT
);
INSERT INTO oauth2_consent_session (id, challenge_id, client_id, subject, authorized, granted, requested_at, responded_at, expires_at, form_data, requested_scopes, granted_scopes, requested_audience, granted_audience)
SELECT id, challenge_id, client_id, subject, authorized, granted, requested_at, responded_at, expires_at, form_data, requested_scopes, granted_scopes, requested_audience, granted_audience
FROM _bkp_UP_V0005_oauth2_consent_session
ORDER BY id;
DROP INDEX oauth2_consent_session_challenge_id_key;
CREATE UNIQUE INDEX oauth2_consent_session_challenge_id_key ON oauth2_consent_session (challenge_id);
DROP TABLE _bkp_UP_V0005_oauth2_consent_session;
ALTER TABLE oauth2_authorization_code_session RENAME TO _bkp_UP_V0005_oauth2_authorization_code_session;
CREATE TABLE IF NOT EXISTS oauth2_authorization_code_session (
id INTEGER,
challenge_id CHAR(36) NOT NULL,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
signature VARCHAR(255) NOT NULL,
subject CHAR(36) NOT NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
requested_scopes TEXT NOT NULL,
granted_scopes TEXT NOT NULL,
requested_audience TEXT NULL DEFAULT '',
granted_audience TEXT NULL DEFAULT '',
active BOOLEAN NOT NULL DEFAULT FALSE,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BLOB NOT NULL,
PRIMARY KEY (id),
CONSTRAINT oauth2_authorization_code_session_challenge_id_fkey
FOREIGN KEY(challenge_id)
REFERENCES oauth2_consent_session(challenge_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT oauth2_authorization_code_session_subject_fkey
FOREIGN KEY(subject)
REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT
);
INSERT INTO oauth2_authorization_code_session (id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data)
SELECT id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data
FROM _bkp_UP_V0005_oauth2_authorization_code_session
ORDER BY id;
DROP INDEX oauth2_authorization_code_session_request_id_idx;
DROP INDEX oauth2_authorization_code_session_client_id_idx;
DROP INDEX oauth2_authorization_code_session_client_id_subject_idx;
CREATE INDEX oauth2_authorization_code_session_request_id_idx ON oauth2_authorization_code_session (request_id);
CREATE INDEX oauth2_authorization_code_session_client_id_idx ON oauth2_authorization_code_session (client_id);
CREATE INDEX oauth2_authorization_code_session_client_id_subject_idx ON oauth2_authorization_code_session (client_id, subject);
DROP TABLE _bkp_UP_V0005_oauth2_authorization_code_session;
ALTER TABLE oauth2_access_token_session RENAME TO _bkp_UP_V0005_oauth2_access_token_session;
CREATE TABLE IF NOT EXISTS oauth2_access_token_session (
id INTEGER,
challenge_id CHAR(36) NOT NULL,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
signature VARCHAR(255) NOT NULL,
subject CHAR(36) NOT NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
requested_scopes TEXT NOT NULL,
granted_scopes TEXT NOT NULL,
requested_audience TEXT NULL DEFAULT '',
granted_audience TEXT NULL DEFAULT '',
active BOOLEAN NOT NULL DEFAULT FALSE,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BLOB NOT NULL,
PRIMARY KEY (id),
CONSTRAINT oauth2_access_token_session_challenge_id_fkey
FOREIGN KEY(challenge_id)
REFERENCES oauth2_consent_session(challenge_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT oauth2_access_token_session_subject_fkey
FOREIGN KEY(subject)
REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT
);
INSERT INTO oauth2_access_token_session (id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data)
SELECT id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data
FROM _bkp_UP_V0005_oauth2_access_token_session
ORDER BY id;
DROP INDEX oauth2_access_token_session_request_id_idx;
DROP INDEX oauth2_access_token_session_client_id_idx;
DROP INDEX oauth2_access_token_session_client_id_subject_idx;
CREATE INDEX oauth2_access_token_session_request_id_idx ON oauth2_access_token_session (request_id);
CREATE INDEX oauth2_access_token_session_client_id_idx ON oauth2_access_token_session (client_id);
CREATE INDEX oauth2_access_token_session_client_id_subject_idx ON oauth2_access_token_session (client_id, subject);
DROP TABLE _bkp_UP_V0005_oauth2_access_token_session;
ALTER TABLE oauth2_refresh_token_session RENAME TO _bkp_UP_V0005_oauth2_refresh_token_session;
CREATE TABLE IF NOT EXISTS oauth2_refresh_token_session (
id INTEGER,
challenge_id CHAR(36) NOT NULL,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
signature VARCHAR(255) NOT NULL,
subject CHAR(36) NOT NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
requested_scopes TEXT NOT NULL,
granted_scopes TEXT NOT NULL,
requested_audience TEXT NULL DEFAULT '',
granted_audience TEXT NULL DEFAULT '',
active BOOLEAN NOT NULL DEFAULT FALSE,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BLOB NOT NULL,
PRIMARY KEY (id),
CONSTRAINT oauth2_refresh_token_session_challenge_id_fkey
FOREIGN KEY(challenge_id)
REFERENCES oauth2_consent_session(challenge_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT oauth2_refresh_token_session_subject_fkey
FOREIGN KEY(subject)
REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT
);
INSERT INTO oauth2_refresh_token_session (id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data)
SELECT id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data
FROM _bkp_UP_V0005_oauth2_refresh_token_session
ORDER BY id;
DROP INDEX oauth2_refresh_token_session_request_id_idx;
DROP INDEX oauth2_refresh_token_session_client_id_idx;
DROP INDEX oauth2_refresh_token_session_client_id_subject_idx;
CREATE INDEX oauth2_refresh_token_session_request_id_idx ON oauth2_refresh_token_session (request_id);
CREATE INDEX oauth2_refresh_token_session_client_id_idx ON oauth2_refresh_token_session (client_id);
CREATE INDEX oauth2_refresh_token_session_client_id_subject_idx ON oauth2_refresh_token_session (client_id, subject);
DROP TABLE _bkp_UP_V0005_oauth2_refresh_token_session;
ALTER TABLE oauth2_pkce_request_session RENAME TO _bkp_UP_V0005_oauth2_pkce_request_session;
CREATE TABLE IF NOT EXISTS oauth2_pkce_request_session (
id INTEGER,
challenge_id CHAR(36) NOT NULL,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
signature VARCHAR(255) NOT NULL,
subject CHAR(36) NOT NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
requested_scopes TEXT NOT NULL,
granted_scopes TEXT NOT NULL,
requested_audience TEXT NULL DEFAULT '',
granted_audience TEXT NULL DEFAULT '',
active BOOLEAN NOT NULL DEFAULT FALSE,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BLOB NOT NULL,
PRIMARY KEY (id),
CONSTRAINT oauth2_pkce_request_session_challenge_id_fkey
FOREIGN KEY(challenge_id)
REFERENCES oauth2_consent_session(challenge_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT oauth2_pkce_request_session_subject_fkey
FOREIGN KEY(subject)
REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT
);
INSERT INTO oauth2_pkce_request_session (id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data)
SELECT id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data
FROM _bkp_UP_V0005_oauth2_pkce_request_session
ORDER BY id;
DROP INDEX oauth2_pkce_request_session_request_id_idx;
DROP INDEX oauth2_pkce_request_session_client_id_idx;
DROP INDEX oauth2_pkce_request_session_client_id_subject_idx;
CREATE INDEX oauth2_pkce_request_session_request_id_idx ON oauth2_pkce_request_session (request_id);
CREATE INDEX oauth2_pkce_request_session_client_id_idx ON oauth2_pkce_request_session (client_id);
CREATE INDEX oauth2_pkce_request_session_client_id_subject_idx ON oauth2_pkce_request_session (client_id, subject);
DROP TABLE _bkp_UP_V0005_oauth2_pkce_request_session;
ALTER TABLE oauth2_openid_connect_session RENAME TO _bkp_UP_V0005_oauth2_openid_connect_session;
CREATE TABLE IF NOT EXISTS oauth2_openid_connect_session (
id INTEGER,
challenge_id CHAR(36) NOT NULL,
request_id VARCHAR(40) NOT NULL,
client_id VARCHAR(255) NOT NULL,
signature VARCHAR(255) NOT NULL,
subject CHAR(36) NOT NULL,
requested_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
requested_scopes TEXT NOT NULL,
granted_scopes TEXT NOT NULL,
requested_audience TEXT NULL DEFAULT '',
granted_audience TEXT NULL DEFAULT '',
active BOOLEAN NOT NULL DEFAULT FALSE,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
form_data TEXT NOT NULL,
session_data BLOB NOT NULL,
PRIMARY KEY (id),
CONSTRAINT oauth2_openid_connect_session_challenge_id_fkey
FOREIGN KEY(challenge_id)
REFERENCES oauth2_consent_session(challenge_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT oauth2_openid_connect_session_subject_fkey
FOREIGN KEY(subject)
REFERENCES user_opaque_identifier(identifier) ON UPDATE RESTRICT ON DELETE RESTRICT
);
INSERT INTO oauth2_openid_connect_session (id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data)
SELECT id, challenge_id, request_id, client_id, signature, subject, requested_at, requested_scopes, granted_scopes, requested_audience, granted_audience, active, revoked, form_data, session_data
FROM _bkp_UP_V0005_oauth2_openid_connect_session
ORDER BY id;
DROP INDEX oauth2_openid_connect_session_request_id_idx;
DROP INDEX oauth2_openid_connect_session_client_id_idx;
DROP INDEX oauth2_openid_connect_session_client_id_subject_idx;
CREATE INDEX oauth2_openid_connect_session_request_id_idx ON oauth2_openid_connect_session (request_id);
CREATE INDEX oauth2_openid_connect_session_client_id_idx ON oauth2_openid_connect_session (client_id);
CREATE INDEX oauth2_openid_connect_session_client_id_subject_idx ON oauth2_openid_connect_session (client_id, subject);
DROP TABLE _bkp_UP_V0005_oauth2_openid_connect_session;
COMMIT;
PRAGMA foreign_keys=on;

View File

@ -47,6 +47,7 @@ type Provider interface {
LoadPreferredDuoDevice(ctx context.Context, username string) (device *model.DuoDevice, err error) LoadPreferredDuoDevice(ctx context.Context, username string) (device *model.DuoDevice, err error)
SaveOAuth2ConsentSession(ctx context.Context, consent model.OAuth2ConsentSession) (err error) SaveOAuth2ConsentSession(ctx context.Context, consent model.OAuth2ConsentSession) (err error)
SaveOAuth2ConsentSessionSubject(ctx context.Context, consent model.OAuth2ConsentSession) (err error)
SaveOAuth2ConsentSessionResponse(ctx context.Context, consent model.OAuth2ConsentSession, rejection bool) (err error) SaveOAuth2ConsentSessionResponse(ctx context.Context, consent model.OAuth2ConsentSession, rejection bool) (err error)
SaveOAuth2ConsentSessionGranted(ctx context.Context, id int) (err error) SaveOAuth2ConsentSessionGranted(ctx context.Context, id int) (err error)
LoadOAuth2ConsentSessionByChallengeID(ctx context.Context, challengeID uuid.UUID) (consent *model.OAuth2ConsentSession, err error) LoadOAuth2ConsentSessionByChallengeID(ctx context.Context, challengeID uuid.UUID) (consent *model.OAuth2ConsentSession, err error)

View File

@ -105,6 +105,7 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa
sqlDeactivateOAuth2OpenIDConnectSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2OpenIDConnectSession), sqlDeactivateOAuth2OpenIDConnectSessionByRequestID: fmt.Sprintf(queryFmtDeactivateOAuth2SessionByRequestID, tableOAuth2OpenIDConnectSession),
sqlInsertOAuth2ConsentSession: fmt.Sprintf(queryFmtInsertOAuth2ConsentSession, tableOAuth2ConsentSession), sqlInsertOAuth2ConsentSession: fmt.Sprintf(queryFmtInsertOAuth2ConsentSession, tableOAuth2ConsentSession),
sqlUpdateOAuth2ConsentSessionSubject: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionSubject, tableOAuth2ConsentSession),
sqlUpdateOAuth2ConsentSessionResponse: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionResponse, tableOAuth2ConsentSession), sqlUpdateOAuth2ConsentSessionResponse: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionResponse, tableOAuth2ConsentSession),
sqlUpdateOAuth2ConsentSessionGranted: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionGranted, tableOAuth2ConsentSession), sqlUpdateOAuth2ConsentSessionGranted: fmt.Sprintf(queryFmtUpdateOAuth2ConsentSessionGranted, tableOAuth2ConsentSession),
sqlSelectOAuth2ConsentSessionByChallengeID: fmt.Sprintf(queryFmtSelectOAuth2ConsentSessionByChallengeID, tableOAuth2ConsentSession), sqlSelectOAuth2ConsentSessionByChallengeID: fmt.Sprintf(queryFmtSelectOAuth2ConsentSessionByChallengeID, tableOAuth2ConsentSession),
@ -235,6 +236,7 @@ type SQLProvider struct {
// Table: oauth2_consent_session. // Table: oauth2_consent_session.
sqlInsertOAuth2ConsentSession string sqlInsertOAuth2ConsentSession string
sqlUpdateOAuth2ConsentSessionSubject string
sqlUpdateOAuth2ConsentSessionResponse string sqlUpdateOAuth2ConsentSessionResponse string
sqlUpdateOAuth2ConsentSessionGranted string sqlUpdateOAuth2ConsentSessionGranted string
sqlSelectOAuth2ConsentSessionByChallengeID string sqlSelectOAuth2ConsentSessionByChallengeID string
@ -390,7 +392,7 @@ func (p *SQLProvider) LoadUserOpaqueIdentifierBySignature(ctx context.Context, s
return opaqueID, nil return opaqueID, nil
} }
// SaveOAuth2ConsentSession inserts an OAuth2.0 consent. // SaveOAuth2ConsentSession inserts an OAuth2.0 consent session.
func (p *SQLProvider) SaveOAuth2ConsentSession(ctx context.Context, consent model.OAuth2ConsentSession) (err error) { func (p *SQLProvider) SaveOAuth2ConsentSession(ctx context.Context, consent model.OAuth2ConsentSession) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlInsertOAuth2ConsentSession, if _, err = p.db.ExecContext(ctx, p.sqlInsertOAuth2ConsentSession,
consent.ChallengeID, consent.ClientID, consent.Subject, consent.Authorized, consent.Granted, consent.ChallengeID, consent.ClientID, consent.Subject, consent.Authorized, consent.Granted,
@ -402,10 +404,18 @@ func (p *SQLProvider) SaveOAuth2ConsentSession(ctx context.Context, consent mode
return nil return nil
} }
// SaveOAuth2ConsentSessionResponse updates an OAuth2.0 consent with the consent response. // SaveOAuth2ConsentSessionSubject updates an OAuth2.0 consent session with the subject.
func (p *SQLProvider) SaveOAuth2ConsentSessionSubject(ctx context.Context, consent model.OAuth2ConsentSession) (err error) {
if _, err = p.db.ExecContext(ctx, p.sqlUpdateOAuth2ConsentSessionSubject, consent.Subject, consent.ID); err != nil {
return fmt.Errorf("error updating oauth2 consent session subject with id '%d' and challenge id '%s' for subject '%s': %w", consent.ID, consent.ChallengeID, consent.Subject, err)
}
return nil
}
// SaveOAuth2ConsentSessionResponse updates an OAuth2.0 consent session with the response.
func (p *SQLProvider) SaveOAuth2ConsentSessionResponse(ctx context.Context, consent model.OAuth2ConsentSession, authorized bool) (err error) { func (p *SQLProvider) SaveOAuth2ConsentSessionResponse(ctx context.Context, consent model.OAuth2ConsentSession, authorized bool) (err error) {
_, err = p.db.ExecContext(ctx, p.sqlUpdateOAuth2ConsentSessionResponse, authorized, consent.ExpiresAt, consent.GrantedScopes, consent.GrantedAudience, consent.ID) if _, err = p.db.ExecContext(ctx, p.sqlUpdateOAuth2ConsentSessionResponse, authorized, consent.ExpiresAt, consent.GrantedScopes, consent.GrantedAudience, consent.ID); err != nil {
if err != nil {
return fmt.Errorf("error updating oauth2 consent session (authorized '%t') with id '%d' and challenge id '%s' for subject '%s': %w", authorized, consent.ID, consent.ChallengeID, consent.Subject, err) return fmt.Errorf("error updating oauth2 consent session (authorized '%t') with id '%d' and challenge id '%s' for subject '%s': %w", authorized, consent.ID, consent.ChallengeID, consent.Subject, err)
} }

View File

@ -75,6 +75,7 @@ func NewPostgreSQLProvider(config *schema.Configuration) (provider *PostgreSQLPr
provider.sqlSelectEncryptionValue = provider.db.Rebind(provider.sqlSelectEncryptionValue) provider.sqlSelectEncryptionValue = provider.db.Rebind(provider.sqlSelectEncryptionValue)
provider.sqlInsertOAuth2ConsentSession = provider.db.Rebind(provider.sqlInsertOAuth2ConsentSession) provider.sqlInsertOAuth2ConsentSession = provider.db.Rebind(provider.sqlInsertOAuth2ConsentSession)
provider.sqlUpdateOAuth2ConsentSessionSubject = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionSubject)
provider.sqlUpdateOAuth2ConsentSessionResponse = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionResponse) provider.sqlUpdateOAuth2ConsentSessionResponse = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionResponse)
provider.sqlUpdateOAuth2ConsentSessionGranted = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionGranted) provider.sqlUpdateOAuth2ConsentSessionGranted = provider.db.Rebind(provider.sqlUpdateOAuth2ConsentSessionGranted)
provider.sqlSelectOAuth2ConsentSessionByChallengeID = provider.db.Rebind(provider.sqlSelectOAuth2ConsentSessionByChallengeID) provider.sqlSelectOAuth2ConsentSessionByChallengeID = provider.db.Rebind(provider.sqlSelectOAuth2ConsentSessionByChallengeID)

View File

@ -240,6 +240,11 @@ const (
form_data, requested_scopes, granted_scopes, requested_audience, granted_audience) form_data, requested_scopes, granted_scopes, requested_audience, granted_audience)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);` VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`
queryFmtUpdateOAuth2ConsentSessionSubject = `
UPDATE %s
SET subject = ?
WHERE id = ?;`
queryFmtUpdateOAuth2ConsentSessionResponse = ` queryFmtUpdateOAuth2ConsentSessionResponse = `
UPDATE %s UPDATE %s
SET authorized = ?, responded_at = CURRENT_TIMESTAMP, expires_at = ?, granted_scopes = ?, granted_audience = ? SET authorized = ?, responded_at = CURRENT_TIMESTAMP, expires_at = ?, granted_scopes = ?, granted_audience = ?