11package controller
22
33import (
4+ "database/sql"
45 "encoding/json"
56 "errors"
67 "fmt"
78 "net/http"
89 "slices"
910 "strings"
11+ "time"
1012
1113 "github.com/gin-gonic/gin"
1214 "github.com/gin-gonic/gin/binding"
@@ -31,6 +33,8 @@ type OIDCController struct {
3133 log * logger.Logger
3234 oidc * service.OIDCService
3335 runtime model.RuntimeConfig
36+ helpers model.RuntimeHelpers
37+ config model.Config
3438}
3539
3640type AuthorizeCallback struct {
@@ -68,10 +72,11 @@ type ClientCredentials struct {
6872}
6973
7074type AuthorizeScreenParams struct {
71- LoginFor FrontendLoginFor `url:"login_for"`
72- OIDCTicket string `url:"oidc_ticket"`
73- OIDCScope string `url:"oidc_scope"`
74- OIDCName string `url:"oidc_name"`
75+ LoginFor FrontendLoginFor `url:"login_for"`
76+ OIDCTicket string `url:"oidc_ticket"`
77+ OIDCScope string `url:"oidc_scope"`
78+ OIDCName string `url:"oidc_name"`
79+ OIDCShowConsent bool `url:"oidc_show_consent"`
7580}
7681
7782type AuthorizeCompleteRequest struct {
@@ -82,12 +87,16 @@ func NewOIDCController(
8287 log * logger.Logger ,
8388 oidcService * service.OIDCService ,
8489 runtimeConfig model.RuntimeConfig ,
90+ helpers model.RuntimeHelpers ,
91+ config model.Config ,
8592 router * gin.RouterGroup ,
8693 mainRouter * gin.RouterGroup ) * OIDCController {
8794 controller := & OIDCController {
8895 log : log ,
8996 oidc : oidcService ,
9097 runtime : runtimeConfig ,
98+ helpers : helpers ,
99+ config : config ,
91100 }
92101
93102 mainRouter .POST ("/authorize" , controller .authorize )
@@ -163,11 +172,31 @@ func (controller *OIDCController) authorize(c *gin.Context) {
163172
164173 ticket := controller .oidc .CreateAuthorizeRequestTicket (* req )
165174
175+ // Check if we have consented before for this client and scope
176+ consnetCookie , err := c .Cookie (controller .runtime .ConsentCookieName )
177+
178+ showConsent := true
179+
180+ if err == nil {
181+ consentEntry , err := controller .oidc .GetConsentEntry (c , consnetCookie )
182+
183+ if err == nil && consentEntry != nil {
184+ if consentEntry .ClientID == req .ClientID && consentEntry .Scopes == req .Scope {
185+ showConsent = false
186+ }
187+ } else {
188+ if ! errors .Is (err , sql .ErrNoRows ) {
189+ controller .log .App .Error ().Err (err ).Msg ("Failed to get consent entry for consent cookie" )
190+ }
191+ }
192+ }
193+
166194 queries , err := query .Values (AuthorizeScreenParams {
167- LoginFor : FrontendLoginForOIDC ,
168- OIDCTicket : ticket ,
169- OIDCScope : req .Scope ,
170- OIDCName : client .Name ,
195+ LoginFor : FrontendLoginForOIDC ,
196+ OIDCTicket : ticket ,
197+ OIDCScope : req .Scope ,
198+ OIDCName : client .Name ,
199+ OIDCShowConsent : showConsent ,
171200 })
172201
173202 if err != nil {
@@ -289,6 +318,33 @@ func (controller *OIDCController) authorizeComplete(c *gin.Context) {
289318 return
290319 }
291320
321+ // Just before returning let's set the consent cookie
322+ consnetUUID , err := controller .oidc .CreateConsentEntry (c , authorizeReq .ClientID , authorizeReq .Scope )
323+
324+ // If we fail to create the consent entry, we don't want to block the authorization flow,
325+ // but we log the error and move on without setting the cookie
326+ if err == nil {
327+ cookieDomain , err := controller .helpers .GetCookieDomain (c .Request .Context (), c .RemoteIP ())
328+
329+ if err == nil {
330+ cookie := & http.Cookie {
331+ Name : controller .runtime .ConsentCookieName ,
332+ Value : consnetUUID ,
333+ Path : "/" ,
334+ Domain : cookieDomain ,
335+ Expires : time .Now ().Add (365 * 24 * time .Hour ), // set consent cookie for 1 year
336+ Secure : controller .config .Auth .SecureCookie ,
337+ HttpOnly : true ,
338+ SameSite : http .SameSiteLaxMode ,
339+ }
340+ http .SetCookie (c .Writer , cookie )
341+ } else {
342+ controller .log .App .Error ().Err (err ).Msg ("Failed to determine cookie domain for consent cookie" )
343+ }
344+ } else {
345+ controller .log .App .Error ().Err (err ).Msg ("Failed to create consent entry" )
346+ }
347+
292348 c .JSON (200 , gin.H {
293349 "status" : 200 ,
294350 "redirect_uri" : fmt .Sprintf ("%s?%s" , authorizeReq .RedirectURI , queries .Encode ()),
0 commit comments