@@ -15,18 +15,23 @@ import (
1515 "github.com/rs/zerolog/log"
1616)
1717
18+ // OIDCControllerConfig holds configuration for the OIDC controller.
1819type OIDCControllerConfig struct {
19- AppURL string
20- CookieDomain string
20+ AppURL string // Base URL of the application
21+ CookieDomain string // Domain for setting cookies
2122}
2223
24+ // OIDCController handles OpenID Connect (OIDC) protocol endpoints.
25+ // It implements the OIDC provider functionality including discovery, authorization,
26+ // token exchange, userinfo, and JWKS endpoints.
2327type OIDCController struct {
2428 config OIDCControllerConfig
2529 router * gin.RouterGroup
2630 oidc * service.OIDCService
2731 auth * service.AuthService
2832}
2933
34+ // NewOIDCController creates a new OIDC controller with the given configuration and services.
3035func NewOIDCController (config OIDCControllerConfig , router * gin.RouterGroup , oidc * service.OIDCService , auth * service.AuthService ) * OIDCController {
3136 return & OIDCController {
3237 config : config ,
@@ -36,6 +41,13 @@ func NewOIDCController(config OIDCControllerConfig, router *gin.RouterGroup, oid
3641 }
3742}
3843
44+ // SetupRoutes registers all OIDC endpoints with the router.
45+ // This includes:
46+ // - /.well-known/openid-configuration - OIDC discovery endpoint
47+ // - /oidc/authorize - Authorization endpoint
48+ // - /oidc/token - Token endpoint
49+ // - /oidc/userinfo - UserInfo endpoint
50+ // - /oidc/jwks - JSON Web Key Set endpoint
3951func (controller * OIDCController ) SetupRoutes () {
4052 // Well-known discovery endpoint
4153 controller .router .GET ("/.well-known/openid-configuration" , controller .discoveryHandler )
@@ -48,6 +60,10 @@ func (controller *OIDCController) SetupRoutes() {
4860 oidcGroup .GET ("/jwks" , controller .jwksHandler )
4961}
5062
63+ // discoveryHandler handles the OIDC discovery endpoint.
64+ // Returns the OpenID Connect discovery document as specified in RFC 8414.
65+ // The document contains metadata about the OIDC provider including endpoints,
66+ // supported features, and cryptographic capabilities.
5167func (controller * OIDCController ) discoveryHandler (c * gin.Context ) {
5268 issuer := controller .oidc .GetIssuer ()
5369 baseURL := strings .TrimSuffix (controller .config .AppURL , "/" )
@@ -70,6 +86,14 @@ func (controller *OIDCController) discoveryHandler(c *gin.Context) {
7086 c .JSON (http .StatusOK , discovery )
7187}
7288
89+ // authorizeHandler handles the OIDC authorization endpoint.
90+ // Implements the authorization code flow as specified in OAuth 2.0 RFC 6749.
91+ // Validates client credentials, redirect URI, scopes, and response type.
92+ // Supports PKCE (RFC 7636) for enhanced security.
93+ // If the user is not authenticated, redirects to the login page with the
94+ // authorization request parameters preserved for redirect after login.
95+ // On success, generates an authorization code and redirects to the client's
96+ // redirect URI with the code and state parameter.
7397func (controller * OIDCController ) authorizeHandler (c * gin.Context ) {
7498 // Get query parameters
7599 clientID := c .Query ("client_id" )
@@ -179,6 +203,11 @@ func (controller *OIDCController) authorizeHandler(c *gin.Context) {
179203 c .Redirect (http .StatusFound , redirectURL .String ())
180204}
181205
206+ // tokenHandler handles the OIDC token endpoint.
207+ // Exchanges an authorization code for access and ID tokens.
208+ // Validates the authorization code, client credentials, redirect URI, and PKCE verifier.
209+ // Returns an access token and optionally an ID token (if openid scope is present).
210+ // Implements the authorization code grant type as specified in OAuth 2.0 RFC 6749.
182211func (controller * OIDCController ) tokenHandler (c * gin.Context ) {
183212 // Get grant type
184213 grantType := c .PostForm ("grant_type" )
@@ -299,6 +328,10 @@ func (controller *OIDCController) tokenHandler(c *gin.Context) {
299328 c .JSON (http .StatusOK , response )
300329}
301330
331+ // userinfoHandler handles the OIDC UserInfo endpoint.
332+ // Returns user information claims for the authenticated user based on the
333+ // provided access token. Validates the access token signature, issuer, and expiration.
334+ // Returns standard OIDC claims: sub, email, name, and preferred_username.
302335func (controller * OIDCController ) userinfoHandler (c * gin.Context ) {
303336 // Get access token from Authorization header or query parameter
304337 accessToken := controller .getAccessToken (c )
@@ -310,8 +343,14 @@ func (controller *OIDCController) userinfoHandler(c *gin.Context) {
310343 return
311344 }
312345
313- // Validate and parse access token
314- userContext , err := controller .validateAccessToken (accessToken )
346+ // Get optional client_id from request for audience validation
347+ clientID := c .Query ("client_id" )
348+ if clientID == "" {
349+ clientID = c .PostForm ("client_id" )
350+ }
351+
352+ // Validate and parse access token with audience validation
353+ userContext , err := controller .oidc .ValidateAccessTokenForClient (accessToken , clientID )
315354 if err != nil {
316355 log .Error ().Err (err ).Msg ("Failed to validate access token" )
317356 c .JSON (http .StatusUnauthorized , gin.H {
@@ -332,6 +371,9 @@ func (controller *OIDCController) userinfoHandler(c *gin.Context) {
332371 c .JSON (http .StatusOK , userInfo )
333372}
334373
374+ // jwksHandler handles the JSON Web Key Set (JWKS) endpoint.
375+ // Returns the public keys used to verify ID tokens and access tokens.
376+ // The keys are in JWK format as specified in RFC 7517.
335377func (controller * OIDCController ) jwksHandler (c * gin.Context ) {
336378 jwks , err := controller .oidc .GetJWKS ()
337379 if err != nil {
@@ -347,6 +389,9 @@ func (controller *OIDCController) jwksHandler(c *gin.Context) {
347389
348390// Helper functions
349391
392+ // redirectError redirects the user to the redirect URI with an error response.
393+ // Includes the error code, error description, and state parameter (if provided).
394+ // If the redirect URI is invalid or empty, returns a JSON error response instead.
350395func (controller * OIDCController ) redirectError (c * gin.Context , redirectURI string , state string , errorCode string , errorDescription string ) {
351396 if redirectURI == "" {
352397 c .JSON (http .StatusBadRequest , gin.H {
@@ -376,13 +421,21 @@ func (controller *OIDCController) redirectError(c *gin.Context, redirectURI stri
376421 c .Redirect (http .StatusFound , redirectURL .String ())
377422}
378423
424+ // tokenError returns a JSON error response for token endpoint errors.
425+ // Uses the standard OAuth 2.0 error format with error and error_description fields.
379426func (controller * OIDCController ) tokenError (c * gin.Context , errorCode string , errorDescription string ) {
380427 c .JSON (http .StatusBadRequest , gin.H {
381428 "error" : errorCode ,
382429 "error_description" : errorDescription ,
383430 })
384431}
385432
433+ // getClientCredentials extracts client credentials from the request.
434+ // Supports client_secret_basic (HTTP Basic Authentication) and
435+ // client_secret_post (POST form parameters) as specified in the discovery document.
436+ // Does not accept credentials via query parameters for security reasons
437+ // (they may be logged in access logs, browser history, or referrer headers).
438+ // Returns the client ID, client secret, and an error if credentials are not found.
386439func (controller * OIDCController ) getClientCredentials (c * gin.Context ) (string , string , error ) {
387440 // Try Basic Auth first (client_secret_basic)
388441 authHeader := c .GetHeader ("Authorization" )
@@ -409,6 +462,10 @@ func (controller *OIDCController) getClientCredentials(c *gin.Context) (string,
409462 return "" , "" , fmt .Errorf ("client credentials not found" )
410463}
411464
465+ // getAccessToken extracts the access token from the request.
466+ // Checks the Authorization header (Bearer token) first, then falls back to
467+ // the access_token query parameter.
468+ // Returns an empty string if no access token is found.
412469func (controller * OIDCController ) getAccessToken (c * gin.Context ) string {
413470 // Try Authorization header
414471 authHeader := c .GetHeader ("Authorization" )
@@ -420,9 +477,13 @@ func (controller *OIDCController) getAccessToken(c *gin.Context) string {
420477 return c .Query ("access_token" )
421478}
422479
480+ // validateAccessToken validates an access token and extracts user context.
481+ // Verifies the JWT signature using the OIDC service's public key, checks the
482+ // issuer, and validates expiration. Returns the user context if valid, or an
483+ // error if validation fails.
423484func (controller * OIDCController ) validateAccessToken (accessToken string ) (* config.UserContext , error ) {
424485 // Validate the JWT token using the OIDC service's public key
425486 // This properly verifies the signature, issuer, and expiration
487+ // Note: This method does not validate audience - use ValidateAccessTokenForClient for that
426488 return controller .oidc .ValidateAccessToken (accessToken )
427489}
428-
0 commit comments