Skip to content

Commit 0cd00dc

Browse files
authored
feat: reduce size of verifiers (#3857)
1 parent 7f8bd90 commit 0cd00dc

24 files changed

+839
-811
lines changed

consent/handler.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package consent
55

66
import (
7+
"context"
78
"encoding/json"
89
"net/http"
910
"net/url"
@@ -476,11 +477,12 @@ func (h *Handler) acceptOAuth2LoginRequest(w http.ResponseWriter, r *http.Reques
476477
}
477478
handledLoginRequest.RequestedAt = loginRequest.RequestedAt
478479

479-
f, err := flowctx.Decode[flow.Flow](ctx, h.r.FlowCipher(), challenge, flowctx.AsLoginChallenge)
480+
f, err := h.decodeFlowWithClient(ctx, challenge, flowctx.AsLoginChallenge)
480481
if err != nil {
481482
h.r.Writer().WriteError(w, r, err)
482483
return
483484
}
485+
484486
request, err := h.r.ConsentManager().HandleLoginRequest(ctx, f, challenge, &handledLoginRequest)
485487
if err != nil {
486488
h.r.Writer().WriteError(w, r, errorsx.WithStack(err))
@@ -575,7 +577,7 @@ func (h *Handler) rejectOAuth2LoginRequest(w http.ResponseWriter, r *http.Reques
575577
return
576578
}
577579

578-
f, err := flowctx.Decode[flow.Flow](ctx, h.r.FlowCipher(), challenge, flowctx.AsLoginChallenge)
580+
f, err := h.decodeFlowWithClient(ctx, challenge, flowctx.AsLoginChallenge)
579581
if err != nil {
580582
h.r.Writer().WriteError(w, r, err)
581583
return
@@ -761,7 +763,7 @@ func (h *Handler) acceptOAuth2ConsentRequest(w http.ResponseWriter, r *http.Requ
761763
p.RequestedAt = cr.RequestedAt
762764
p.HandledAt = sqlxx.NullTime(time.Now().UTC())
763765

764-
f, err := flowctx.Decode[flow.Flow](ctx, h.r.FlowCipher(), challenge, flowctx.AsConsentChallenge)
766+
f, err := h.decodeFlowWithClient(ctx, challenge, flowctx.AsConsentChallenge)
765767
if err != nil {
766768
h.r.Writer().WriteError(w, r, err)
767769
return
@@ -868,7 +870,7 @@ func (h *Handler) rejectOAuth2ConsentRequest(w http.ResponseWriter, r *http.Requ
868870
return
869871
}
870872

871-
f, err := flowctx.Decode[flow.Flow](ctx, h.r.FlowCipher(), challenge, flowctx.AsConsentChallenge)
873+
f, err := h.decodeFlowWithClient(ctx, challenge, flowctx.AsConsentChallenge)
872874
if err != nil {
873875
h.r.Writer().WriteError(w, r, err)
874876
return
@@ -1044,3 +1046,17 @@ func (h *Handler) getOAuth2LogoutRequest(w http.ResponseWriter, r *http.Request,
10441046

10451047
h.r.Writer().Write(w, r, request)
10461048
}
1049+
1050+
func (h *Handler) decodeFlowWithClient(ctx context.Context, challenge string, opts ...flowctx.CodecOption) (*flow.Flow, error) {
1051+
f, err := flowctx.Decode[flow.Flow](ctx, h.r.FlowCipher(), challenge, opts...)
1052+
if err != nil {
1053+
return nil, err
1054+
}
1055+
1056+
f.Client, err = h.r.ClientManager().GetConcreteClient(ctx, f.ClientID)
1057+
if err != nil {
1058+
return nil, err
1059+
}
1060+
1061+
return f, nil
1062+
}

consent/strategy_default.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ func (s *DefaultStrategy) verifyAuthentication(
348348
return nil, errorsx.WithStack(fosite.ErrAccessDenied.WithHint("The login verifier is invalid."))
349349
}
350350

351+
f.Client, err = s.r.ClientManager().GetConcreteClient(ctx, f.ClientID)
352+
if err != nil {
353+
return nil, err
354+
}
355+
351356
session, err := s.r.ConsentManager().VerifyAndInvalidateLoginRequest(ctx, verifier)
352357
if errors.Is(err, sqlcon.ErrNoRows) {
353358
return nil, errorsx.WithStack(fosite.ErrAccessDenied.WithHint("The login verifier has already been used, has not been granted, or is invalid."))
@@ -652,6 +657,12 @@ func (s *DefaultStrategy) verifyConsent(ctx context.Context, _ http.ResponseWrit
652657
if err != nil {
653658
return nil, nil, errorsx.WithStack(fosite.ErrAccessDenied.WithHint("The consent verifier has already been used, has not been granted, or is invalid."))
654659
}
660+
661+
f.Client, err = s.r.ClientManager().GetConcreteClient(ctx, f.ClientID)
662+
if err != nil {
663+
return nil, nil, err
664+
}
665+
655666
if f.Client.GetID() != r.URL.Query().Get("client_id") {
656667
return nil, nil, errorsx.WithStack(fosite.ErrInvalidClient.WithHint("The flow client id does not match the authorize request client id."))
657668
}
@@ -1170,13 +1181,3 @@ func (s *DefaultStrategy) ObfuscateSubjectIdentifier(ctx context.Context, cl fos
11701181
}
11711182
return subject, nil
11721183
}
1173-
1174-
func (s *DefaultStrategy) loginSessionFromCookie(r *http.Request) *flow.LoginSession {
1175-
clientID := r.URL.Query().Get("client_id")
1176-
if clientID == "" {
1177-
return nil
1178-
}
1179-
ls, _ := flowctx.FromCookie[flow.LoginSession](r.Context(), r, s.r.FlowCipher(), flowctx.LoginSessionCookie(flowctx.SuffixFromStatic(clientID)))
1180-
1181-
return ls
1182-
}

flow/flow.go

Lines changed: 60 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -84,92 +84,91 @@ type Flow struct {
8484
// identify the session.
8585
//
8686
// required: true
87-
ID string `db:"login_challenge"`
88-
NID uuid.UUID `db:"nid"`
87+
ID string `db:"login_challenge" json:"i"`
88+
NID uuid.UUID `db:"nid" json:"n"`
8989

9090
// RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client.
9191
//
9292
// required: true
93-
RequestedScope sqlxx.StringSliceJSONFormat `db:"requested_scope"`
93+
RequestedScope sqlxx.StringSliceJSONFormat `db:"requested_scope" json:"rs,omitempty"`
9494

9595
// RequestedAudience contains the access token audience as requested by the OAuth 2.0 Client.
9696
//
9797
// required: true
98-
RequestedAudience sqlxx.StringSliceJSONFormat `db:"requested_at_audience"`
98+
RequestedAudience sqlxx.StringSliceJSONFormat `db:"requested_at_audience" json:"ra,omitempty"`
9999

100100
// LoginSkip, if true, implies that the client has requested the same scopes from the same user previously.
101101
// If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.
102102
//
103103
// This feature allows you to update / set session information.
104104
//
105105
// required: true
106-
LoginSkip bool `db:"login_skip"`
106+
LoginSkip bool `db:"login_skip" json:"ls,omitempty"`
107107

108108
// Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope
109109
// requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type
110110
// when accepting the login request, or the request will fail.
111111
//
112112
// required: true
113-
Subject string `db:"subject"`
113+
Subject string `db:"subject" json:"s,omitempty"`
114114

115115
// OpenIDConnectContext provides context for the (potential) OpenID Connect context. Implementation of these
116116
// values in your app are optional but can be useful if you want to be fully compliant with the OpenID Connect spec.
117-
OpenIDConnectContext *OAuth2ConsentRequestOpenIDConnectContext `db:"oidc_context"`
117+
OpenIDConnectContext *OAuth2ConsentRequestOpenIDConnectContext `db:"oidc_context" json:"oc"`
118118

119119
// Client is the OAuth 2.0 Client that initiated the request.
120120
//
121121
// required: true
122-
Client *client.Client `db:"-"`
123-
124-
ClientID string `db:"client_id"`
122+
Client *client.Client `db:"-" json:"client,omitempty"`
123+
ClientID string `db:"client_id" json:"ci,omitempty"`
125124

126125
// RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which
127126
// initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but
128127
// might come in handy if you want to deal with additional request parameters.
129128
//
130129
// required: true
131-
RequestURL string `db:"request_url"`
130+
RequestURL string `db:"request_url" json:"r,omitempty"`
132131

133132
// SessionID is the login session ID. If the user-agent reuses a login session (via cookie / remember flag)
134133
// this ID will remain the same. If the user-agent did not have an existing authentication session (e.g. remember is false)
135134
// this will be a new random value. This value is used as the "sid" parameter in the ID Token and in OIDC Front-/Back-
136135
// channel logout. Its value can generally be used to associate consecutive login requests by a certain user.
137-
SessionID sqlxx.NullString `db:"login_session_id"`
136+
SessionID sqlxx.NullString `db:"login_session_id" json:"si,omitempty"`
138137

139138
// IdentityProviderSessionID is the session ID of the end-user that authenticated.
140139
// If specified, we will use this value to propagate the logout.
141-
IdentityProviderSessionID sqlxx.NullString `db:"identity_provider_session_id"`
140+
IdentityProviderSessionID sqlxx.NullString `db:"identity_provider_session_id" json:"is,omitempty"`
142141

143-
LoginVerifier string `db:"login_verifier"`
144-
LoginCSRF string `db:"login_csrf"`
142+
LoginVerifier string `db:"login_verifier" json:"lv,omitempty"`
143+
LoginCSRF string `db:"login_csrf" json:"lc,omitempty"`
145144

146-
LoginInitializedAt sqlxx.NullTime `db:"login_initialized_at"`
147-
RequestedAt time.Time `db:"requested_at"`
145+
LoginInitializedAt sqlxx.NullTime `db:"login_initialized_at" json:"li,omitempty"`
146+
RequestedAt time.Time `db:"requested_at" json:"ia,omitempty"`
148147

149-
State int16 `db:"state"`
148+
State int16 `db:"state" json:"q,omitempty"`
150149

151150
// LoginRemember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store
152151
// a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she
153152
// will not be asked to log in again.
154-
LoginRemember bool `db:"login_remember"`
153+
LoginRemember bool `db:"login_remember" json:"lr,omitempty"`
155154

156155
// LoginRememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the
157156
// authorization will be remembered for the duration of the browser session (using a session cookie).
158-
LoginRememberFor int `db:"login_remember_for"`
157+
LoginRememberFor int `db:"login_remember_for" json:"lf,omitempty"`
159158

160159
// LoginExtendSessionLifespan, if set to true, session cookie expiry time will be updated when session is
161160
// refreshed (login skip=true).
162-
LoginExtendSessionLifespan bool `db:"login_extend_session_lifespan"`
161+
LoginExtendSessionLifespan bool `db:"login_extend_session_lifespan" json:"ll,omitempty"`
163162

164163
// ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it
165164
// to express that, for example, a user authenticated using two factor authentication.
166-
ACR string `db:"acr"`
165+
ACR string `db:"acr" json:"a,omitempty"`
167166

168167
// AMR sets the Authentication Methods References value for this
169168
// authentication session. You can use it to specify the method a user used to
170169
// authenticate. For example, if the acr indicates a user used two factor
171170
// authentication, the amr can express they used a software-secured key.
172-
AMR sqlxx.StringSliceJSONFormat `db:"amr"`
171+
AMR sqlxx.StringSliceJSONFormat `db:"amr" json:"am,omitempty"`
173172

174173
// ForceSubjectIdentifier forces the "pairwise" user ID of the end-user that authenticated. The "pairwise" user ID refers to the
175174
// (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID
@@ -188,58 +187,58 @@ type Flow struct {
188187
// other unique value).
189188
//
190189
// If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.
191-
ForceSubjectIdentifier string `db:"forced_subject_identifier"`
190+
ForceSubjectIdentifier string `db:"forced_subject_identifier" json:"fs,omitempty"`
192191

193192
// Context is an optional object which can hold arbitrary data. The data will be made available when fetching the
194193
// consent request under the "context" field. This is useful in scenarios where login and consent endpoints share
195194
// data.
196-
Context sqlxx.JSONRawMessage `db:"context"`
195+
Context sqlxx.JSONRawMessage `db:"context" json:"ct"`
197196

198197
// LoginWasUsed set to true means that the login request was already handled.
199198
// This can happen on form double-submit or other errors. If this is set we
200199
// recommend redirecting the user to `request_url` to re-initiate the flow.
201-
LoginWasUsed bool `db:"login_was_used"`
200+
LoginWasUsed bool `db:"login_was_used" json:"lu,omitempty"`
202201

203-
LoginError *RequestDeniedError `db:"login_error"`
204-
LoginAuthenticatedAt sqlxx.NullTime `db:"login_authenticated_at"`
202+
LoginError *RequestDeniedError `db:"login_error" json:"le,omitempty"`
203+
LoginAuthenticatedAt sqlxx.NullTime `db:"login_authenticated_at" json:"la,omitempty"`
205204

206205
// ConsentChallengeID is the identifier ("authorization challenge") of the consent authorization request. It is used to
207206
// identify the session.
208207
//
209208
// required: true
210-
ConsentChallengeID sqlxx.NullString `db:"consent_challenge_id"`
209+
ConsentChallengeID sqlxx.NullString `db:"consent_challenge_id" json:"cc,omitempty"`
211210

212211
// ConsentSkip, if true, implies that the client has requested the same scopes from the same user previously.
213212
// If true, you must not ask the user to grant the requested scopes. You must however either allow or deny the
214213
// consent request using the usual API call.
215-
ConsentSkip bool `db:"consent_skip"`
216-
ConsentVerifier sqlxx.NullString `db:"consent_verifier"`
217-
ConsentCSRF sqlxx.NullString `db:"consent_csrf"`
214+
ConsentSkip bool `db:"consent_skip" json:"cs,omitempty"`
215+
ConsentVerifier sqlxx.NullString `db:"consent_verifier" json:"cv,omitempty"`
216+
ConsentCSRF sqlxx.NullString `db:"consent_csrf" json:"cr,omitempty"`
218217

219218
// GrantedScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`.
220-
GrantedScope sqlxx.StringSliceJSONFormat `db:"granted_scope"`
219+
GrantedScope sqlxx.StringSliceJSONFormat `db:"granted_scope" json:"gs,omitempty"`
221220

222221
// GrantedAudience sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`.
223-
GrantedAudience sqlxx.StringSliceJSONFormat `db:"granted_at_audience"`
222+
GrantedAudience sqlxx.StringSliceJSONFormat `db:"granted_at_audience" json:"ga,omitempty"`
224223

225224
// ConsentRemember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same
226225
// client asks the same user for the same, or a subset of, scope.
227-
ConsentRemember bool `db:"consent_remember"`
226+
ConsentRemember bool `db:"consent_remember" json:"ce,omitempty"`
228227

229228
// ConsentRememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the
230229
// authorization will be remembered indefinitely.
231-
ConsentRememberFor *int `db:"consent_remember_for"`
230+
ConsentRememberFor *int `db:"consent_remember_for" json:"cf"`
232231

233232
// ConsentHandledAt contains the timestamp the consent request was handled.
234-
ConsentHandledAt sqlxx.NullTime `db:"consent_handled_at"`
233+
ConsentHandledAt sqlxx.NullTime `db:"consent_handled_at" json:"ch,omitempty"`
235234

236235
// ConsentWasHandled set to true means that the request was already handled.
237236
// This can happen on form double-submit or other errors. If this is set we
238237
// recommend redirecting the user to `request_url` to re-initiate the flow.
239-
ConsentWasHandled bool `db:"consent_was_used"`
240-
ConsentError *RequestDeniedError `db:"consent_error"`
241-
SessionIDToken sqlxx.MapStringInterface `db:"session_id_token" faker:"-"`
242-
SessionAccessToken sqlxx.MapStringInterface `db:"session_access_token" faker:"-"`
238+
ConsentWasHandled bool `db:"consent_was_used" json:"cw,omitempty"`
239+
ConsentError *RequestDeniedError `db:"consent_error" json:"cx"`
240+
SessionIDToken sqlxx.MapStringInterface `db:"session_id_token" faker:"-" json:"st"`
241+
SessionAccessToken sqlxx.MapStringInterface `db:"session_access_token" faker:"-" json:"sa"`
243242
}
244243

245244
func NewFlow(r *LoginRequest) *Flow {
@@ -511,21 +510,37 @@ type CipherProvider interface {
511510
}
512511

513512
// ToLoginChallenge converts the flow into a login challenge.
514-
func (f *Flow) ToLoginChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) {
513+
func (f Flow) ToLoginChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) {
514+
if f.Client != nil {
515+
f.ClientID = f.Client.GetID()
516+
}
517+
f.Client = nil
515518
return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsLoginChallenge)
516519
}
517520

518521
// ToLoginVerifier converts the flow into a login verifier.
519-
func (f *Flow) ToLoginVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) {
522+
func (f Flow) ToLoginVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) {
523+
if f.Client != nil {
524+
f.ClientID = f.Client.GetID()
525+
}
526+
f.Client = nil
520527
return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsLoginVerifier)
521528
}
522529

523530
// ToConsentChallenge converts the flow into a consent challenge.
524-
func (f *Flow) ToConsentChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) {
531+
func (f Flow) ToConsentChallenge(ctx context.Context, cipherProvider CipherProvider) (string, error) {
532+
if f.Client != nil {
533+
f.ClientID = f.Client.GetID()
534+
}
535+
f.Client = nil
525536
return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsConsentChallenge)
526537
}
527538

528539
// ToConsentVerifier converts the flow into a consent verifier.
529-
func (f *Flow) ToConsentVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) {
540+
func (f Flow) ToConsentVerifier(ctx context.Context, cipherProvider CipherProvider) (string, error) {
541+
if f.Client != nil {
542+
f.ClientID = f.Client.GetID()
543+
}
544+
f.Client = nil
530545
return flowctx.Encode(ctx, cipherProvider.FlowCipher(), f, flowctx.AsConsentVerifier)
531546
}

oauth2/flowctx/cookies.go

Lines changed: 0 additions & 34 deletions
This file was deleted.

0 commit comments

Comments
 (0)