I'm encountering an issue with the "on behalf of" (OBO) authentication flow in my application. My infrastructure consists of the following components:
I've implemented the standard AzureADProvider
in my api/auth/[...nextauth]/route.ts
file. I've parameterized it with the client ID, client secret, and tenant ID from my application registration. Everything is functioning smoothly and I'm able to obtain a token from Azure. However, this token is intended for the Microsoft Graph resource, making it unsuitable for authenticating requests to my .NET 6 API.
To manage with this I would like to use the on-behalf-of flow to acquire an appropriate token for my API resource. Below is the next-auth JWT callback code:
const authority = `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}`;
const config = {
auth: {
clientId: process.env.AZURE_AD_CLIENT_ID as string,
authority,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET as string,
knownAuthorities: [authority],
},
};
const cca = new msal.ConfidentialClientApplication(config);
const acquireOnBehalfOfAccessToken = async (assertion: string) =>
await cca
.acquireTokenOnBehalfOf({
oboAssertion: assertion,
scopes: [process.env.API_SCOPE as string],
})
.catch((err) => console.log(err));
export const authOptions: NextAuthOptions = {
...
callbacks: {
async jwt({ token, user, account }) {
if (account && account.access_token && account.expires_at) {
if (Date.now() < account.expires_at * 1000) {
token.apiTokenDetails = await acquireOnBehalfOfAccessToken(
account.access_token as string
);
return token;
}
}
return token;
},
},
};
Unfortunately, I'm encountering an error:
AADSTS50013: Assertion failed signature validation. [Reason - The key was not found., Thumbprint of key used by client: 'xxxx'
Surprisingly, I'm able to obtain my API token using a refresh token through the following method:
const generateApiAccessToken = async (refreshToken: string) =>
await cca
.acquireTokenByRefreshToken({
scopes: [process.env.API_SCOPE as string],
refreshToken,
})
.catch((err) => console.log(err));
How is possible, that I'm able to retrieve an access token using the refresh token method, yet I'm encountering difficulties with the on-behalf-of flow. The OBO flow seems like a more elegant approach for implementing authentication in both my React and .NET applications.
The error may occur if you pass wrong assertion generated with scopes other than exposed API, to get access token in on-behalf of flow.
I have one application named Web API
where I exposed API scopes as below:
Now I registered one client application and added API permissions as below:
Now, I generated access token using authorization code flow with API scope
via Postman:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id: <OBO_app_ID>
grant_type:authorization_code
scope: api://xxxxxxxxxxx/custom.scope
code: code
redirect_uri: https://jwt.ms
client_secret: <secret>
Response:
You can decode the above token in jwt.ms to check aud
and scp
claims:
Now, pass above access token as assertion
value in on-behalf of flow with Microsoft Graph scope. I generated access token using on-behalf of flow via Postman with below parameters:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id: <API_app_ID>
client_secret: <API_app_secret>
scope: https://graph.microsoft.com/.default
grant_type: urn:ietf:params:oauth:grant-type:jwt-bearer
assertion: <paste_token_from_above_request>
requested_token_use: on_behalf_of
Response:
To confirm that, I decoded this access token in jwt.ms and got audience of Microsoft Graph with User.Read in scp
claim.
When I used this token to call Microsoft graph, I got response successfully like below:
GET https://graph.microsoft.com/v1.0/me
Response:
In your case, you need to first generate access token from middle-tier API with exposed API scope and use it as assertion
for the OBO call to acquire token for Microsoft Graph.
References:
Microsoft identity platform and OAuth2.0 On-Behalf-Of flow
azure active directory - On behalf of flow returns AADSTS50013 - Stack Overflow by Chauncy Zhou