I've only been able to figure out how to get an id token using B2C - but then I lose all the benefits of regular AAD apps (specifically access tokens, scopes and user consent)
Below I'll describe a simplified scenario, and what I've tried.
Imagine I am developing a client (javascript SPA) and two services (WebAPI):
Now I want the user to sign in using Azure B2C, in a way which yields my client C the following tokens:
Can this be done? And how? Any examples out there?
All the examples I've been able to find shows one client authenticating the user, and a few show how to get a single access token (no scope support)
My experiments sofar have been carried out using
fooplanner.onmicrosoft.com
AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA
BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB
FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF
oidc-client.js
- I would have used adal.js, but it doesn't suppport B2C (and besides, it's apparently being superceded by MSA, which doesn't even support Javascript yet...)To avoid confusion relating to client-side libraries, my experiments below will be described in terms of the requests and responses sent and received, as reported by Fiddler.
This is the baseline scenario, shown by most of the how-tos I've found.
Client C sends the following request to B2C:
https://login.microsoftonline.com/fooplanner.onmicrosoft.com/oauth2/v2.0/authorize?
p=b2c_1_fooplanner-signuporsignin&
client_id=ffffffff-ffff-ffff-ffff-ffffffffffff&
response_type=id_token&
scope=openid email profile
After prompting me for my credentials, B2C then eventually returns a single id_token
(where uuu...
is the GUID for my user entry in B2C):
id-token:
{
"ver": "1.0",
"iss": "https://login.microsoftonline.com/08de3e5f-6a10-4d7c-a0e3-fc4a627a712b/v2.0/",
"sub": "uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu",
"aud": "ffffffff-ffff-ffff-ffff-ffffffffffff",
"oid": "uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu",
"name": "Thomas"
"tfp": "B2C_1_fooplanner-signuporsignin"
}
(for brevity, I've omitted all the OAuth2 redirects, the Base64 JWT decoding etc. - I've even left out timestamps, nonces etc from the tokens. If they're relevant, I can supply full details)
This is received and handled as expected by oidc-client.js: I end up with an ID token, and no access token.
After a little digging, I found a way to get an access token too: include the B2C application ID in the scopes, and ask for both token
and id_token
response types.
In this variant, client C sends the following request to B2C:
https://login.microsoftonline.com/fooplanner.onmicrosoft.com/oauth2/v2.0/authorize?
p=b2c_1_fooplanner-signuporsignin&
client_id=ffffffff-ffff-ffff-ffff-ffffffffffff&
response_type=token id_token&
scope=openid email profile ffffffff-ffff-ffff-ffff-ffffffffffff
B2C then eventually returns an id_token
and an access_token
:
id_token:
{
"ver": "1.0",
"iss": "https://login.microsoftonline.com/08de3e5f-6a10-4d7c-a0e3-fc4a627a712b/v2.0/",
"sub": "uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu",
"aud": "ffffffff-ffff-ffff-ffff-ffffffffffff",
"oid": "uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu",
"name": "Thomas"
"tfp": "B2C_1_fooplanner-signuporsignin"
}
access_token:
{
"iss": "https://login.microsoftonline.com/08de3e5f-6a10-4d7c-a0e3-fc4a627a712b/v2.0/",
"aud": "ffffffff-ffff-ffff-ffff-ffffffffffff",
"oid": "uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu",
"sub": "uuuuuuuu-uuuu-uuuu-uuuu-uuuuuuuuuuuu",
"name": "Thomas",
"tfp": "B2C_1_fooplanner-signuporsignin",
}
This is again received and handled as expected by oidc-client.js: I end up with an ID token and an access token.
Notice however how suspiciously familiar the two tokens are - but then again, I'm asking for an access token for a B2C application, not a properly registered (AAD) application.
So I thought: let's follow the previous approach - only this time, ask for an access token for one of the two real services.
Request:
https://login.microsoftonline.com/fooplanner.onmicrosoft.com/oauth2/v2.0/authorize?
p=b2c_1_fooplanner-signuporsignin&
client_id=ffffffff-ffff-ffff-ffff-ffffffffffff&
response_type=token id_token&
scope=openid email profile aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
This however complains about an unknown scope (aaa...
) - so either I'm misusing the protocol, or B2C doesn't know about regular AAD apps (in the same tenant, mind you).
I've read somewhere (the OpenID spec?) that an IdP (i.e. B2C) has an authorization endpoint that you can use to exchange an id_token
for an access_token
.
Would this be the way to approach this? And are there any client-side libraries out there supporting this?
Azure AD B2C launched support for access tokens and custom defined scopes: https://azure.microsoft.com/en-us/blog/azure-ad-b2c-access-tokens-now-in-public-preview/ https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-access-tokens
However, there is still a limitation that you can only request an access token for one application at a time so it would be two requests. (But since the user authenticated with B2C, the second request would include a cookie and B2C would silently redirect back to the relying party application)
https://login.microsoftonline.com/fooplanner.onmicrosoft.com/oauth2/v2.0/authorize?
p=b2c_1_fooplanner-signuporsignin&
client_id=ffffffff-ffff-ffff-ffff-ffffffffffff&
response_type=id_token token&
scope=openid https://fooplanner.onmicrosoft.com/webapi1/readscopeA https://fooplanner.onmicrosoft.com/webapi1/writescopeA profile
https://login.microsoftonline.com/fooplanner.onmicrosoft.com/oauth2/v2.0/authorize?
p=b2c_1_fooplanner-signuporsignin&
client_id=ffffffff-ffff-ffff-ffff-ffffffffffff&
response_type=id_token token&
scope=openid https://fooplanner.onmicrosoft.com/webapi2/readscopeB https://fooplanner.onmicrosoft.com/webapi2/writescopeB profile
If the response_type parameter in a authorize request includes “token”, the “scope” parameter must include at least one resource permission (other than “openid” and “offline_access”) that will be granted. Otherwise, the authorize request will terminate with a failure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With