mod_auth_openidc
mod_auth_openidc copied to clipboard
SPA: retrieving access token with a subset of refresh token scopes
Hello,
Following Vivien's question here: RFC6749 allows requesting a new access token from a refresh token with fewer permissions:
1.5. Refresh Token
Refresh tokens are credentials used to obtain access tokens. Refresh tokens are issued to the client by the authorization server and are used to obtain a new access token when the current access token becomes invalid or expires, or to obtain additional access tokens with identical or narrower scope (access tokens may have a shorter lifetime and fewer permissions than authorized by the resource owner).
Section 6 of this RFC indeed allows a scope parameter when requesting new access token:
- Refreshing an Access Token
[...] grant_type REQUIRED. Value MUST be set to "refresh_token".
refresh_token REQUIRED. The refresh token issued to the client.
scope OPTIONAL. The scope of the access request as described by Section 3.3. The requested scope MUST NOT include any scope not originally granted by the resource owner, and if omitted is treated as equal to the scope originally granted by the resource owner.
It may be wanted to call some externals APIs from javascript with an RFC6750 Bearer access token as credential to access this API - which in return access some data on our "own" API (for example: allowing this external API to access phone number on "my" /userinfo API, and only phone number through the phone scope). However, the initial refresh token may have been granted with wider set of scopes.
It would be nice if mod_auth_openidc could return narrower-scope access tokens from the InfoHook endpoint. This could be done via an additional parameter on this endpoint, such as: <redirect_uri>?info=json&access_token_scope=phone%20myotherscope
Hello,
I looked deeper how scopes are handled in mod_auth_openidc and effectively the list of scopes is statically defined in the configuration file. By default, it is not the backend that requests an AT to the agent, but the agent that passes the AT to the backend in the OIDC_access_token header (only JS apps have to request the AT explicitly). Like this, only one AT at a time is managed per session.
The agent can be asked to refresh the AT on the RefreshHook endpoint (<redirect_uri>?refresh=<return_to>&access_token=<access_token>).
However this option doesn't allow a client to get different AT with reduced scopes because :
- It is not possible to specify the requested scopes.
- If a new AT with reduced scopes is obtained, it overwrites the main one (the one atteched to the session with extended scopes) :
mod_auth_openidccan only manage a single AT at time, and even if it allowed a scope reduction, the new AT would overwrite the main one, which would be problematic if we had to call several API simultaneously with different scopes ...
Similarly, the InfoHook endpoint (<redirect_uri>?info=json), does not meet the need as it is because:
- It is not possible to specify the required scopes
- It is not possible to force the AT renewal (only the AT at the end of their life are refreshed)
- If a new AT with reduced scopes is obtained, it overwrites the main one
To allow the scope reduction, it would therefore expand the possibilities offered by the InfoHook endpoint to allow an application (JS or backend):
- to recover a new AT with custom scopes on demand (in this case
mod_auth_openidcshould force the token renewal using the RT even if a valid AT is cached in the module); - but also, not to modify the session state after getting the new AT with reduced scopes, that is, not to overwrite or discard the main AT.
It seems feasible.
The oidc_handle_info_request() function must be modified to accept custom scopes in request parameters, and force the agent to get a new AT with these scopes whatever the remaining lifetime of the tokens set in memory.
The oidc_refresh_access_token() function have also to be duplicated to a new oidc_refresh_access_token_with_custom_scope() function that would be modified to allow a caller to retieve a new AT with custom scopes, without discarding the main AT linked to the session.
The interface could be:
<redirect_uri>?info=json&custom_scope=scope1%20scope2
I think this evolution could be beneficial to the project. It allows web portals that call different APIs to respect the principle of least privilege that is promoted by OAuth.
@zandbelt What is your view of that? Do you plan to implement such a feature someday?
Many thanks
I have no plans to implement this, I would welcome a PR... But I would like to understand why you think the original access token needs to be retained: it may very well be (that's undefined behavior anyhow) that the Authorization Server invalidates the old access token since it handed out a new token with lesser scopes for that particular client. After all the token swap to reduce scipes is done for security hygiene reasons and hence - as part of that routine - the old token should be discarded by both the Client and the Authorization Server IMO.
stepup authentication with OIDCUnAutzAction can be used to implement such a scenario, the SPA will have to call into differently "scoped" paths to obtain a new access token, see: https://github.com/OpenIDC/mod_auth_openidc/wiki/Step-up-Authentication