AzureAD / microsoft-identity-web

Helps creating protected web apps and web APIs with Microsoft identity platform and Azure AD B2C
MIT License
683 stars 214 forks source link

[Feature Request] Add support for AAD B2C access token acquisition without a client_secret #1352

Open tedvanderveen opened 3 years ago

tedvanderveen commented 3 years ago

Is your feature request related to a problem? Please describe. Azure B2C allows for using a refresh token for obtaining a new access token without supplying a client_secret. See following successfull request/reponse:

POST https://{removed}.b2clogin.com/{removed}.onmicrosoft.com/b2c_1_signup_signin/oauth2/v2.0/token?p=b2c_1_signup_signin HTTP/1.1
Host: {removed}.b2clogin.com
User-Agent: Microsoft ASP.NET Core OpenIdConnect handler
traceparent: 00-12e2cfaf1f2bfd4daab716cfa715f1ac-beda6a6f3ccd8d4f-00
Content-Type: application/x-www-form-urlencoded
Content-Length: 1156

grant_type=refresh_token&refresh_token=eyJraWQiOiJjcGltY29{removed}BxvLG5oeySeDA&client_id=79d4{removed}0abcb&redirect_uri=https%3A%2F%2Flocalhost%3A5001%2Fsignin-oidc&scope=openid 79d4{removed}0abcb

HTTP/1.1 200 OK
Cache-Control: private
Allow: OPTIONS,TRACE,GET,HEAD,POST
Content-Type: application/json; charset=utf-8
x-ms-gateway-requestid: e65aa40c-3fcf-4390-9415-c99abfc49681
Set-Cookie: x-ms-cpim-trans=; domain={removed}.b2clogin.com; expires=Thu, 28-Jul-2011 14:05:54 GMT; path=/; SameSite=None; secure; HttpOnly
X-Frame-Options: DENY
Public: OPTIONS,TRACE,GET,HEAD,POST
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Date: Wed, 28 Jul 2021 14:05:54 GMT
Content-Length: 3803

{"access_token":"eyJ0eXAiOiJKV1QiLCJ{removed}cmTY2gEqWcn3Lw","id_token":"eyJ0eXAiOiJKV1{removed}KFWBVrNYxk3nNtA","token_type":"Bearer","not_before":1627481154,"expires_in":3600,"expires_on":1627484754,"resource":"79d4{removed}0abcb","id_token_expires_in":3600,"profile_info":"eyJ2ZXIiOiIxLjA{removed}wIjpudWxsfQ","scope":"/ openid","refresh_token":"eyJraWQiOiJjcGltY29y{removed}f1ExD-JWYJw","refresh_token_expires_in":85473}

But the class TokenAcquisition requires a client secret or client certificate to be provided. Of not, the following error is thrown: IDW10104: Both client secret and client certificate cannot be null or whitespace, and only ONE must be included in the configuration of the web app when calling a web API. For instance, in the appsettings.json file..

See this hard dependency on "confidential" app configuration here: https://github.com/AzureAD/microsoft-identity-web/blob/14429b8cb93352d78b6e1618d3a0bb50f203602d/src/Microsoft.Identity.Web/TokenAcquisition.cs#L651

Describe the solution you'd like Enable the supported B2C API for acquiring access tokens, as B2C does not require client secrets or certs. This is especially usefull in scenario's where B2C support for PKCE is leveraged and the original refresh token is also obtained without a client_secret

Describe alternatives you've considered Write our own implementation of ITokenAcquisition that does not mandate client_secret or cert.

jmprieur commented 3 years ago

@tedvanderveen

If you are interested in writing a SPA, we suggest you use MSAL.js, not Microsoft.Identity.Web.

tedvanderveen commented 3 years ago

@jmprieur the notion of a "confidential app" and the usage of client_secret in relation to OAuth flows is being deprecated in favor of usage of PKCE. As per the latest version of the latest OAuth 2.0 Security Best Current Practice: https://datatracker.ietf.org/doc/draft-ietf-oauth-security-topics In section 2.1.1. it states:

Public clients MUST use PKCE [RFC7636] to this end. For confidential clients, the use of PKCE [RFC7636] is **RECOMMENDED**.

Additionally, AAD B2C also supports it.

Why would this library allow sign-in and acquire access/refresh tokens on sign-in using PKCE, but suddenly require a client_secret if one wants to actually use that refresh token to obtain a fresh access token?

tedvanderveen commented 3 years ago

For who is actually interested in learning why PKCE is also important for confidential apps, please read up on why the industry is moving in this direction.

https://condatis.com/news/blog/oauth-confidential-clients/

tedvanderveen commented 3 years ago

@jmprieur just to sum things up:

Is this a correct reflection of current design?

Ponant commented 3 years ago

@tedvanderveen , did you try commenting the code?

  MergedOptionsValidation.ValidateEitherClientCertificateOrClientSecret(
                 mergedOptions.ClientSecret,
                 mergedOptions.ClientCertificates);

in

 private IConfidentialClientApplication BuildConfidentialClientApplication(MergedOptions mergedOptions) 

?

Ponant commented 3 years ago

Well, no it throws here

                IConfidentialClientApplication app = builder.Build();
jmprieur commented 3 years ago

For who is actually interested in learning why PKCE is also important for confidential apps, please read up on why the industry is moving in this direction.

https://condatis.com/news/blog/oauth-confidential-clients/

Message received, thanks @tedvanderveen. The current implementation uses a nonce and tokens cannot be replayed, but we can go the extra mile. Having said that last time I checked the middleware (which validates the ID token in a web app) wasn't happy without the ID token.

jmprieur commented 3 years ago

@jmprieur just to sum things up:

  • By design this lib DOES SUPPORT a "public" app to signin users to B2C with PKCE and obtain a refresh_token
  • By desgin this lib DOES NOT SUPPORT a "public" app to do anything useful with that same refresh_token (although B2C itself DOES support this)

Is this a correct reflection of current design?

What is a public app? Ms.id.web is only about confidential client apps?

Ponant commented 3 years ago

@jmprieur , there is also this video in case you did not have a look at: https://www.youtube.com/watch?v=1ot45WwQWJE for example. It uses a real world example of a server app and the guy is just logging in with google.

In short, to me at least, the idea is that PKCE on a confidential app even without an API call should be made possible in the libraries, with the last step of the code_verifier being applied. I see PKCE on a server web app delivering html as better than client sercrets, a bit similar in philosophy to antiforgery tokens in the older days because the code_verifier is a random string that is generated each time, which is better than a one time secret stored in the server.

I understand that there are diverse opinions on this, and I have seen on SO a few answers saying that PKCE on a server app without api calls adds little security, but there are opinions on the opposite, two, and I believe that this is why the specs recommend PKCE for confidential clients regardless of the presence of APIS.

Personnaly I would like to see this implemented.

tedvanderveen commented 3 years ago

@jmprieur just to sum things up:

  • By design this lib DOES SUPPORT a "public" app to signin users to B2C with PKCE and obtain a refresh_token
  • By desgin this lib DOES NOT SUPPORT a "public" app to do anything useful with that same refresh_token (although B2C itself DOES support this)

Is this a correct reflection of current design?

What is a public app? Ms.id.web is only about confidential client apps?

A public App is an app that cannot securely handle confidential secrets i.e. a client_secret for example. And -also for that reason- leverages PKCE flows to solve issues that traditionally that client_secret takes care of. If this lib also fully embraces PKCE flow, then it can also be used by -for example- Blazor WebAssembly public apps.