-
Notifications
You must be signed in to change notification settings - Fork 44
Description
In a relatively obscure passage in RFC6749 it seems that token endpoint may also accept scope parameter:
[3.3](https://www.rfc-editor.org/rfc/rfc6749#section-3.3). Access Token Scope
The authorization and token endpoints allow the client to specify the
scope of the access request using the "scope" request parameter. In
turn, the authorization server uses the "scope" response parameter to
inform the client of the scope of the access token issued.
When requesting multiple scopes for multiple audiences, Microsoft Entra refuses the authorization code to access token exchange with the message
ADSTS28003: Provided value for the input parameter scope cannot be empty
when requesting an access token using the provided authorization code
The application is supposed to re-use the authorization code (or use the refresh token if issued) and call the token endpoint multiple times to obtain multiple access tokens, one per requested scope.
I wonder what should be the usable user interface for this in a tool like oauth2c. MSAL does it in a pretty ugly way (using an extraextra_scopes_to_consent parameter).
Looks like I can't call the token endpoint two times with the same authorization code:
AADSTS54005: OAuth2 Authorization code was already redeemed,
please retry with a new valid code or use an existing refresh token.
The following sequence of curl requests seems to work:
Obtain the authorization code (token endpoint call will fail, but we use the authorization code)
oauth2c "$URL" \
--no-browser \
--client-id "$CLIENT_ID" \
--response-types code \
--response-mode query \
--grant-type authorization_code \
--auth-method none \
--scopes scope1,scope2 \
--authorization-endpoint "${AUTHORIZATION_ENDPOINT}" \
--token-endpoint "${TOKEN_ENDPOINT}" \
--pkceExchange authorization code for scope1 only
curl "${TOKEN_ENDPOINT}" \
-H "Origin: http://localhost:9876" \
-d redirect_uri=http://localhost:9876/callback \
-d code="$code" \
-d code_verifier="$v" \
-d grant_type=authorization_code \
-d client_id=$CLIENT_ID \
-d scope=scope1 > token1
REFRESH_TOKEN="$(jq -r .refresh_token < token1)"Exchange the refresh token for scope2 only
curl "${TOKEN_ENDPOINT}" \
-H "Origin: http://localhost:9876" \
-d refresh_token="$REFRESH_TOKEN" \
-d grant_type=refresh_token \
-d client_id=$CLIENT_ID \
-d scope=scope2 > token2Refresh token for scope1
R1=$(jq -r .refresh_token token1)
oauth2c "$URL" \
--no-browser \
--client-id "$CLIENT_ID" \
--grant-type refresh_token \
--token-endpoint "${TOKEN_ENDPOINT}" \
--auth-method none \
--refresh-token "$R1"Refresh token for scope2
R2=$(jq -r .refresh_token token2)
oauth2c "$URL" \
--no-browser \
--client-id "$CLIENT_ID" \
--grant-type refresh_token \
--token-endpoint "${TOKEN_ENDPOINT}" \
--auth-method none \
--refresh-token "$R2"It looks like the refresh token "knows" which scope it should generate the updated access token for, but it is possible to specify the scope parameter anyway:
R2=$(jq -r .refresh_token token2)
oauth2c "$URL" \
--no-browser \
--client-id "$CLIENT_ID" \
--grant-type refresh_token \
--token-endpoint "${TOKEN_ENDPOINT}" \
--auth-method none \
--scope scope1 \
--refresh-token "$R2"This also works and generates a new access token for scope1 and so on...
One implementation idea would be:
-
We could add
--token-scopesparameter (the actual name of the parameter would bescope) which could be added automatically for any calls to the token endpoint. -
The
--grant_type authorization_codecould send--scopesto the authorization endpoint and--token-scopesto the token endpoint (both as thescopeparameter). -
The
--grant_type refresh_tokencould send--token-scopesto the token endpoint. -
Implicit grant never calls the token endpoint, so the
--token-scopescould be ignored (or not accepted) -
Client credentials - I don't know - in that case
--scopesand--token-scopesmean the same. -
Other flows - no idea yet.