diff --git a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl index 46062c893..3d83711db 100644 --- a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl +++ b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc.erl @@ -33,6 +33,7 @@ <<"content-type">> => <<"text/plain">> }). -define(REDIRECT_BODY, <<"Redirecting...">>). +-define(PKCE_VERIFIER_LEN, 60). %%------------------------------------------------------------------------------ %% Hocon Schema @@ -84,6 +85,11 @@ fields(oidc) -> ?HOCON(emqx_schema:timeout_duration_ms(), #{ desc => ?DESC(session_expiry), default => <<"30s">> + })}, + {require_pkce, + ?HOCON(boolean(), #{ + desc => ?DESC(require_pkce), + default => false })} ]; fields(login) -> @@ -133,25 +139,27 @@ login( config := #{ clientid := ClientId, secret := Secret, - scopes := Scopes + scopes := Scopes, + require_pkce := RequirePKCE } } = Cfg ) -> Nonce = emqx_dashboard_sso_oidc_session:random_bin(), - Data = #{nonce => Nonce}, + Opts = maybe_require_pkce(RequirePKCE, #{ + scopes => Scopes, + nonce => Nonce, + redirect_uri => emqx_dashboard_sso_oidc_api:make_callback_url(Cfg) + }), + Data = maps:with([nonce, require_pkce, pkce_verifier], Opts), State = emqx_dashboard_sso_oidc_session:new(Data), + case oidcc:create_redirect_url( ?PROVIDER_SVR_NAME, ClientId, Secret, - #{ - scopes => Scopes, - state => State, - nonce => Nonce, - redirect_uri => emqx_dashboard_sso_oidc_api:make_callback_url(Cfg) - } + Opts#{state => State} ) of {ok, [Base, Delimiter, Params]} -> @@ -164,3 +172,11 @@ login( convert_certs(_Dir, Conf) -> Conf. + +maybe_require_pkce(false, Opts) -> + Opts; +maybe_require_pkce(true, Opts) -> + Opts#{ + require_pkce => true, + pkce_verifier => emqx_dashboard_sso_oidc_session:random_bin(?PKCE_VERIFIER_LEN) + }. diff --git a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl index a2b795aea..a886e7777 100644 --- a/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl +++ b/apps/emqx_dashboard_sso/src/emqx_dashboard_sso_oidc_api.erl @@ -115,7 +115,7 @@ ensure_oidc_state(#{<<"state">> := State} = QS, Cfg) -> retrieve_token( #{<<"code">> := Code}, #{name := Name, config := #{clientid := ClientId, secret := Secret}} = Cfg, - #{nonce := Nonce} = _Data + Data ) -> case oidcc:retrieve_token( @@ -123,7 +123,7 @@ retrieve_token( Name, ClientId, Secret, - #{redirect_uri => make_callback_url(Cfg), nonce => Nonce} + Data#{redirect_uri => make_callback_url(Cfg)} ) of {ok, Token} -> diff --git a/rel/i18n/emqx_dashboard_sso_oidc.hocon b/rel/i18n/emqx_dashboard_sso_oidc.hocon index 797829317..cacec9617 100644 --- a/rel/i18n/emqx_dashboard_sso_oidc.hocon +++ b/rel/i18n/emqx_dashboard_sso_oidc.hocon @@ -21,4 +21,7 @@ dashboard_addr.desc: session_expiry.desc: """The valid time span for an OIDC `state`, the default is `30s`, if the code response returned by the authorization server exceeds this time span, it will be treated as invalid.""" +require_pkce.desc: +"""Whether to require PKCE when getting the token.""" + }