From da6a206e0bbb88ad721bb3226a075ceebbaeebf8 Mon Sep 17 00:00:00 2001 From: Mario Gruber Date: Wed, 27 Nov 2024 15:05:22 +0100 Subject: [PATCH 1/2] Allow to enable Proof Key for Code Exachange (PKCE) Wires usePkceWithAuthorizationCodeGrant OAuth2 option of the Swagger UI to the options interface --- README.md | 19 +++++++------ swagger.go | 76 ++++++++++++++++++++++++++++--------------------- swagger_test.go | 13 +++++++++ 3 files changed, 66 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 9c20ddd..a94c123 100644 --- a/README.md +++ b/README.md @@ -195,12 +195,13 @@ func main() { } ``` -| Option | Type | Default | Description | -| ------------------------ | ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| URL | string | "doc.json" | URL pointing to API definition | -| DocExpansion | string | "list" | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). | -| DeepLinking | bool | true | If set to true, enables deep linking for tags and operations. See the Deep Linking documentation for more information. | -| DefaultModelsExpandDepth | int | 1 | Default expansion depth for models (set to -1 completely hide the models). | -| InstanceName | string | "swagger" | The instance name of the swagger document. If multiple different swagger instances should be deployed on one gin router, ensure that each instance has a unique name (use the _--instanceName_ parameter to generate swagger documents with _swag init_). | -| PersistAuthorization | bool | false | If set to true, it persists authorization data and it would not be lost on browser close/refresh. | -| Oauth2DefaultClientID | string | "" | If set, it's used to prepopulate the _client_id_ field of the OAuth2 Authorization dialog. | +| Option | Type | Default | Description | +| --------------------------------- | ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| URL | string | "doc.json" | URL pointing to API definition | +| DocExpansion | string | "list" | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). | +| DeepLinking | bool | true | If set to true, enables deep linking for tags and operations. See the Deep Linking documentation for more information. | +| DefaultModelsExpandDepth | int | 1 | Default expansion depth for models (set to -1 completely hide the models). | +| InstanceName | string | "swagger" | The instance name of the swagger document. If multiple different swagger instances should be deployed on one gin router, ensure that each instance has a unique name (use the _--instanceName_ parameter to generate swagger documents with _swag init_). | +| PersistAuthorization | bool | false | If set to true, it persists authorization data and it would not be lost on browser close/refresh. | +| Oauth2DefaultClientID | string | "" | If set, it's used to prepopulate the _client_id_ field of the OAuth2 Authorization dialog. | +| UsePkceWithAuthorizationCodeGrant | bool | false | If set to true, it enables Proof Key for Code Exchange to enhance security for OAuth public clients. | \ No newline at end of file diff --git a/swagger.go b/swagger.go index 9206c78..9b3604e 100644 --- a/swagger.go +++ b/swagger.go @@ -16,27 +16,29 @@ import ( ) type swaggerConfig struct { - URL string - DocExpansion string - Title string - Oauth2RedirectURL htmlTemplate.JS - DefaultModelsExpandDepth int - DeepLinking bool - PersistAuthorization bool - Oauth2DefaultClientID string + URL string + DocExpansion string + Title string + Oauth2RedirectURL htmlTemplate.JS + DefaultModelsExpandDepth int + DeepLinking bool + PersistAuthorization bool + Oauth2DefaultClientID string + UsePkceWithAuthorizationCodeGrant bool } // Config stores ginSwagger configuration variables. type Config struct { // The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`. - URL string - DocExpansion string - InstanceName string - Title string - DefaultModelsExpandDepth int - DeepLinking bool - PersistAuthorization bool - Oauth2DefaultClientID string + URL string + DocExpansion string + InstanceName string + Title string + DefaultModelsExpandDepth int + DeepLinking bool + PersistAuthorization bool + Oauth2DefaultClientID string + UsePkceWithAuthorizationCodeGrant bool } func (config Config) toSwaggerConfig() swaggerConfig { @@ -48,9 +50,10 @@ func (config Config) toSwaggerConfig() swaggerConfig { Oauth2RedirectURL: "`${window.location.protocol}//${window.location.host}$" + "{window.location.pathname.split('/').slice(0, window.location.pathname.split('/').length - 1).join('/')}" + "/oauth2-redirect.html`", - Title: config.Title, - PersistAuthorization: config.PersistAuthorization, - Oauth2DefaultClientID: config.Oauth2DefaultClientID, + Title: config.Title, + PersistAuthorization: config.PersistAuthorization, + Oauth2DefaultClientID: config.Oauth2DefaultClientID, + UsePkceWithAuthorizationCodeGrant: config.UsePkceWithAuthorizationCodeGrant, } } @@ -106,17 +109,26 @@ func Oauth2DefaultClientID(oauth2DefaultClientID string) func(*Config) { } } +// UsePkceWithAuthorizationCodeGrant enables Proof Key for Code Exchange. +// Only applies to accessCode (Authorization Code) flows. +func UsePkceWithAuthorizationCodeGrant(usePkce bool) func(*Config) { + return func(c *Config) { + c.UsePkceWithAuthorizationCodeGrant = usePkce + } +} + // WrapHandler wraps `http.Handler` into `gin.HandlerFunc`. func WrapHandler(handler *webdav.Handler, options ...func(*Config)) gin.HandlerFunc { var config = Config{ - URL: "doc.json", - DocExpansion: "list", - InstanceName: swag.Name, - Title: "Swagger UI", - DefaultModelsExpandDepth: 1, - DeepLinking: true, - PersistAuthorization: false, - Oauth2DefaultClientID: "", + URL: "doc.json", + DocExpansion: "list", + InstanceName: swag.Name, + Title: "Swagger UI", + DefaultModelsExpandDepth: 1, + DeepLinking: true, + PersistAuthorization: false, + Oauth2DefaultClientID: "", + UsePkceWithAuthorizationCodeGrant: false, } for _, c := range options { @@ -270,12 +282,10 @@ window.onload = function() { defaultModelsExpandDepth: {{.DefaultModelsExpandDepth}} }) - const defaultClientId = "{{.Oauth2DefaultClientID}}"; - if (defaultClientId) { - ui.initOAuth({ - clientId: defaultClientId - }) - } + ui.initOAuth({ + clientId: {{.Oauth2DefaultClientID}}, + usePkceWithAuthorizationCodeGrant: {{.UsePkceWithAuthorizationCodeGrant}} + }) window.ui = ui } diff --git a/swagger_test.go b/swagger_test.go index a7a825b..5b3ac68 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -254,3 +254,16 @@ func TestOauth2DefaultClientID(t *testing.T) { configFunc(&cfg) assert.Equal(t, "", cfg.Oauth2DefaultClientID) } + +func TestUsePkceWithAuthorizationCodeGrant(t *testing.T) { + var cfg Config + assert.Equal(t, false, cfg.UsePkceWithAuthorizationCodeGrant) + + configFunc := UsePkceWithAuthorizationCodeGrant(true) + configFunc(&cfg) + assert.Equal(t, true, cfg.UsePkceWithAuthorizationCodeGrant) + + configFunc = UsePkceWithAuthorizationCodeGrant(false) + configFunc(&cfg) + assert.Equal(t, false, cfg.UsePkceWithAuthorizationCodeGrant) +} From 94a6986a649ee2c4336fb8062db44c4e41d27324 Mon Sep 17 00:00:00 2001 From: Mario Gruber Date: Fri, 29 Nov 2024 15:57:28 +0100 Subject: [PATCH 2/2] Changes according to maintainers review --- README.md | 20 ++++++------ swagger.go | 82 ++++++++++++++++++++++++++----------------------- swagger_test.go | 12 ++++---- 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index a94c123..8f268d4 100644 --- a/README.md +++ b/README.md @@ -195,13 +195,13 @@ func main() { } ``` -| Option | Type | Default | Description | -| --------------------------------- | ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| URL | string | "doc.json" | URL pointing to API definition | -| DocExpansion | string | "list" | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). | -| DeepLinking | bool | true | If set to true, enables deep linking for tags and operations. See the Deep Linking documentation for more information. | -| DefaultModelsExpandDepth | int | 1 | Default expansion depth for models (set to -1 completely hide the models). | -| InstanceName | string | "swagger" | The instance name of the swagger document. If multiple different swagger instances should be deployed on one gin router, ensure that each instance has a unique name (use the _--instanceName_ parameter to generate swagger documents with _swag init_). | -| PersistAuthorization | bool | false | If set to true, it persists authorization data and it would not be lost on browser close/refresh. | -| Oauth2DefaultClientID | string | "" | If set, it's used to prepopulate the _client_id_ field of the OAuth2 Authorization dialog. | -| UsePkceWithAuthorizationCodeGrant | bool | false | If set to true, it enables Proof Key for Code Exchange to enhance security for OAuth public clients. | \ No newline at end of file +| Option | Type | Default | Description | +| ------------------------ | ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| URL | string | "doc.json" | URL pointing to API definition | +| DocExpansion | string | "list" | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). | +| DeepLinking | bool | true | If set to true, enables deep linking for tags and operations. See the Deep Linking documentation for more information. | +| DefaultModelsExpandDepth | int | 1 | Default expansion depth for models (set to -1 completely hide the models). | +| InstanceName | string | "swagger" | The instance name of the swagger document. If multiple different swagger instances should be deployed on one gin router, ensure that each instance has a unique name (use the _--instanceName_ parameter to generate swagger documents with _swag init_). | +| PersistAuthorization | bool | false | If set to true, it persists authorization data and it would not be lost on browser close/refresh. | +| Oauth2DefaultClientID | string | "" | If set, it's used to prepopulate the _client_id_ field of the OAuth2 Authorization dialog. | +| Oauth2UsePkce | bool | false | If set to true, it enables Proof Key for Code Exchange to enhance security for OAuth public clients. | \ No newline at end of file diff --git a/swagger.go b/swagger.go index 9b3604e..7e0e414 100644 --- a/swagger.go +++ b/swagger.go @@ -16,29 +16,29 @@ import ( ) type swaggerConfig struct { - URL string - DocExpansion string - Title string - Oauth2RedirectURL htmlTemplate.JS - DefaultModelsExpandDepth int - DeepLinking bool - PersistAuthorization bool - Oauth2DefaultClientID string - UsePkceWithAuthorizationCodeGrant bool + URL string + DocExpansion string + Title string + Oauth2RedirectURL htmlTemplate.JS + DefaultModelsExpandDepth int + DeepLinking bool + PersistAuthorization bool + Oauth2DefaultClientID string + Oauth2UsePkce bool } // Config stores ginSwagger configuration variables. type Config struct { // The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`. - URL string - DocExpansion string - InstanceName string - Title string - DefaultModelsExpandDepth int - DeepLinking bool - PersistAuthorization bool - Oauth2DefaultClientID string - UsePkceWithAuthorizationCodeGrant bool + URL string + DocExpansion string + InstanceName string + Title string + DefaultModelsExpandDepth int + DeepLinking bool + PersistAuthorization bool + Oauth2DefaultClientID string + Oauth2UsePkce bool } func (config Config) toSwaggerConfig() swaggerConfig { @@ -50,10 +50,10 @@ func (config Config) toSwaggerConfig() swaggerConfig { Oauth2RedirectURL: "`${window.location.protocol}//${window.location.host}$" + "{window.location.pathname.split('/').slice(0, window.location.pathname.split('/').length - 1).join('/')}" + "/oauth2-redirect.html`", - Title: config.Title, - PersistAuthorization: config.PersistAuthorization, - Oauth2DefaultClientID: config.Oauth2DefaultClientID, - UsePkceWithAuthorizationCodeGrant: config.UsePkceWithAuthorizationCodeGrant, + Title: config.Title, + PersistAuthorization: config.PersistAuthorization, + Oauth2DefaultClientID: config.Oauth2DefaultClientID, + Oauth2UsePkce: config.Oauth2UsePkce, } } @@ -109,26 +109,27 @@ func Oauth2DefaultClientID(oauth2DefaultClientID string) func(*Config) { } } -// UsePkceWithAuthorizationCodeGrant enables Proof Key for Code Exchange. -// Only applies to accessCode (Authorization Code) flows. -func UsePkceWithAuthorizationCodeGrant(usePkce bool) func(*Config) { +// Oauth2UsePkce enables Proof Key for Code Exchange. +// Corresponds to the usePkceWithAuthorizationCodeGrant property of the Swagger UI +// and applies only to accessCode (Authorization Code) flows. +func Oauth2UsePkce(usePkce bool) func(*Config) { return func(c *Config) { - c.UsePkceWithAuthorizationCodeGrant = usePkce + c.Oauth2UsePkce = usePkce } } // WrapHandler wraps `http.Handler` into `gin.HandlerFunc`. func WrapHandler(handler *webdav.Handler, options ...func(*Config)) gin.HandlerFunc { var config = Config{ - URL: "doc.json", - DocExpansion: "list", - InstanceName: swag.Name, - Title: "Swagger UI", - DefaultModelsExpandDepth: 1, - DeepLinking: true, - PersistAuthorization: false, - Oauth2DefaultClientID: "", - UsePkceWithAuthorizationCodeGrant: false, + URL: "doc.json", + DocExpansion: "list", + InstanceName: swag.Name, + Title: "Swagger UI", + DefaultModelsExpandDepth: 1, + DeepLinking: true, + PersistAuthorization: false, + Oauth2DefaultClientID: "", + Oauth2UsePkce: false, } for _, c := range options { @@ -282,10 +283,13 @@ window.onload = function() { defaultModelsExpandDepth: {{.DefaultModelsExpandDepth}} }) - ui.initOAuth({ - clientId: {{.Oauth2DefaultClientID}}, - usePkceWithAuthorizationCodeGrant: {{.UsePkceWithAuthorizationCodeGrant}} - }) + const defaultClientId = "{{.Oauth2DefaultClientID}}"; + if (defaultClientId) { + ui.initOAuth({ + clientId: defaultClientId, + usePkceWithAuthorizationCodeGrant: {{.Oauth2UsePkce}} + }) + } window.ui = ui } diff --git a/swagger_test.go b/swagger_test.go index 5b3ac68..85c5cb7 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -255,15 +255,15 @@ func TestOauth2DefaultClientID(t *testing.T) { assert.Equal(t, "", cfg.Oauth2DefaultClientID) } -func TestUsePkceWithAuthorizationCodeGrant(t *testing.T) { +func TestOauth2UsePkce(t *testing.T) { var cfg Config - assert.Equal(t, false, cfg.UsePkceWithAuthorizationCodeGrant) + assert.Equal(t, false, cfg.Oauth2UsePkce) - configFunc := UsePkceWithAuthorizationCodeGrant(true) + configFunc := Oauth2UsePkce(true) configFunc(&cfg) - assert.Equal(t, true, cfg.UsePkceWithAuthorizationCodeGrant) + assert.Equal(t, true, cfg.Oauth2UsePkce) - configFunc = UsePkceWithAuthorizationCodeGrant(false) + configFunc = Oauth2UsePkce(false) configFunc(&cfg) - assert.Equal(t, false, cfg.UsePkceWithAuthorizationCodeGrant) + assert.Equal(t, false, cfg.Oauth2UsePkce) }