Is Active Directory not supporting Authorization Code Flow with PKCE?

active-directory, azure-active-directory, oauth-2.0, pkce

Solution

Update 2021

Microsoft finally updated their portal, so we now have a UI to properly configure the Authorization Code Flow with PKCE.

All you have to do is open the Authentication-page of your registered AAD Application. There you can add a new Platform by clicking the button on the left:

Select the new Single-page application tile and enter your redirect urls.

Previous Answer (Manifest)

I just found the answer in the @azure/msal-browser package. At the moment Azure AD seems to be working on supporting this auth flow. To activate it you must set a new type for the redirection urls, that they just recently added.

To use the Authorization Code Flow with PKCE with Azure Active Directory you need to:

- Set add a web plattform to your azure ad application and add your redirect urls.

- Change the type of those redirect urls from 'Web' to 'Spa'. This must be done in the Manifest. Changing it will make the urls disappear from the Authentication Page. But that's ok, since it's still present in the Manifest.

- Treat the web app as a public client (Authentication > Advanded settings > Default client type - 'Yes').

Now the token endpoint doesn't require a `client_secret` or `client_assertion` anymore.

Problem

I tried to use the currently recommended Authorization Code Flow with PKCE to gather an access token from Active Directory. The client will be a public Angular SPA which is the reason for the chosen flow. Gathering the openid-configuration form AD as well as the Authorization Code for a user worked well. But I fail requesting the access token from the following endpoint: ``` https://login.microsoftonline.com/{tenantId}/oauth2/token. ``` I tried to reconstruct the request in Postman: ``` POST /7e8c2868-7490-4dd7-82b7-f5ec29222d30/oauth2/token HTTP/1.1 Host: login.microsoftonline.com Accept: application/json, text/plain, */* Content-Type: application/x-www-form-urlencoded Cache-Control: no-cache grant_type=authorization_code code=... code_verifier=... client_id=... redirect_uri=... ``` ...and end up with the following message: ``` { "error": "invalid_client", "error_description": "AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: ed0413ad-89f1-4a2b-8d68-e23498701800\r\nCorrelation ID: deb53b0d-5398-4f72-a9a5-6c0863547b99\r\nTimestamp: 2020-03-06 09:30:36Z", "error_codes": [ 7000218 ], "timestamp": "2020-03-06 09:30:36Z", "trace_id": "ed0413ad-89f1-4a2b-8d68-e23498701800", "correlation_id": "deb53b0d-5398-4f72-a9a5-6c0863547b99", "error_uri": "https://login.microsoftonline.com/error?code=7000218" } ``` This seems odd, since the official specification of the Authentication Flow with PKCE doesn't require a client_secret or client_assertion. This is only required for the default Authentication Flow. Is there something wrong with the AD implementation or have i misconfigured it? The manifest of the web client looks like this: ``` { "id": "...", "acceptMappedClaims": null, "accessTokenAcceptedVersion": null, "addIns": [], "allowPublicClient": true, "appId": "...", "appRoles": [], "oauth2AllowUrlPathMatching": false, "createdDateTime": "...", "groupMembershipClaims": null, "identifierUris": [], "informationalUrls": { "termsOfService": null, "support": null, "privacy": null, "marketing": null }, "keyCredentials": [], "knownClientApplications": [], "logoUrl": null, "logoutUrl": null, "name": "...", "oauth2AllowIdTokenImplicitFlow": false, "oauth2AllowImplicitFlow": false, "oauth2Permissions": [], "oauth2RequirePostResponse": false, "optionalClaims": null, "orgRestrictions": [], "parentalControlSettings": { "countriesBlockedForMinors": [], "legalAgeGroupRule": "Allow" }, "passwordCredentials": [], "preAuthorizedApplications": [], "publisherDomain": "...", "replyUrlsWithType": [ { "url": "http://localhost:4200", "type": "Web" } ], "requiredResourceAccess": [ { "resourceAppId": "00000003-0000-0000-c000-000000000000", "resourceAccess": [ { "id": "...", "type": "Scope" } ] } ], "samlMetadataUrl": null, "signInUrl": null, "signInAudience": "AzureADMyOrg", "tags": [], "tokenEncryptionKeyId": null } ``` And my application is registered as a public app in AD. The Authentication request that was sent prior to that looked like this: ``` GET /.../oauth2/authorize response_type=code &client_id=... &state=... &redirect_uri=http%3A%2F%2Flocalhost%3A4200 &scope=openid%20user_impersonation%20offline_access &code_challenge=... &code_challenge_method=... &nonce=... Host: login.microsoftonline.com ```

Original source