Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
325 stars 56 forks source link

PKCE code_challenge required for custom authentication provider (B2C) #640

Open ShawInnes opened 2 years ago

ShawInnes commented 2 years ago

Describe the bug

The following error is presented when trying to use Static Web App authentication with Azure B2C as an OpenID Connect provider.

Correlation ID: bfd441e2-7174-4f95-8e3b-179a009aa86e
Timestamp: 2021-11-23 08:43:51Z
AADB2C99059: The supplied request must present a code_challenge

A clear and concise description of what the bug is.

To Reproduce

I've followed the directions here for creating a custom OpenID Connect authentication provider and the flows seem to be working until the point at which it hits B2C.

Steps to reproduce the behavior:

  1. Create an Azure Static Web App
  2. Create an Azure B2C Tenant with App registration and add redirect urls to the callback urls as per the above documentation
  3. Use the b2c configuration declared in the attached staticwebsites.config.json
  4. Create a React App
  5. Have the React App call the login flow with a link to /.auth/login/b2c?post_login_redirect_uri=${window.location.pathname}
  6. The attached error is displayed

note: B2C is correctly configured with app secrets and redirect urls, the app secrets are configured in the Azure Static Web App application configuration section. I've confirmed these are correct by changing them to invalid ones at which point I receive a different error.

Expected behavior

I expect that the authentication request would be accepted by B2C and the user flow would progress as normal.

Screenshots

image

staticwebapp.config.json

{
  "navigationFallback": {
    "rewrite": "/index.html",
    "exclude": [
      "*.{css,scss,js,png,gif,ico,jpg,svg}"
    ]
  },
  "auth": {
    "identityProviders": {
      "customOpenIdConnectProviders": {
        "b2c": {
          "registration": {
            "clientIdSettingName": "B2C_CLIENT_ID",
            "clientCredential": {
              "clientSecretSettingName": "B2C_CLIENT_SECRET"
            },
            "openIdConnectConfiguration": {
              "wellKnownOpenIdConfiguration": "https://redacted.b2clogin.com/redacted.onmicrosoft.com/B2C_1_SUSI/v2.0/.well-known/openid-configuration"
            }
          },
          "login": {
            "nameClaimType": "emails",
            "scopes": ["openid", "profile"]
          }
        }
      }
    }
  }
}
mfcollins3 commented 2 years ago

I'm hitting this now on an app that I created and hosted using Static Web Apps that is backed by Azure AD B2C. Is there a solution yet or a workaround to get it to work?

danwatford commented 2 years ago

SWA with B2C authentication is working for me. The only difference I could spot in staticwebapp.conf.json is

          "login": {
            "nameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
            "scopes": ["openid", "profile"],
            "loginParameterNames": []
          }

On the B2C flow I needed to ensure that the Application Claims included Display Name.

colinansah commented 2 years ago

I'm facing the same issue right now. I have the same staticwebapp.conf.json (different provider).

Is there a way to complete the PKCE flow using the static web app auth? Because I haven't found a way to supply the code_challenge and or code_challenge_method.

mfcollins3 commented 2 years ago

@colinansah When I hit it, it was because my custom policy didn’t include a name or displayName claim in the token (I customized mine and only was returning the bare minimum set of claims). After I added that, it worked.

colinansah commented 2 years ago

@mfcollins3 Thanks but I already have it setup correctly for my custom claim.. It fails at the initial request to the openid authorize endpoint since PKCE is required by my identity provider and code_challenge is missing in the request (I have access to the identity provider and inspected the incoming request).

RossBugginsNHS commented 2 years ago

Changing the app platform type from SPA to web sorted this for me

jacobpoldenzuhlke commented 1 year ago

@ShawInnes did you resolve this?

rubenaster commented 1 year ago

My code currently looks like this:

    "routes": [
      {
        "route": "/*",
        "allowedRoles": ["authenticated"]
      }
    ],
    "responseOverrides": {
      "401": {
        "statusCode": 302,
        "redirect": "/.auth/login/aadb2c"
      }
    },
    "auth": {
        "identityProviders": {
        "customOpenIdConnectProviders": {
          "aadb2c": {
            "registration": {
              "clientIdSettingName": "AADB2C_PROVIDER_CLIENT_ID",
              "clientCredential": {
                "clientSecretSettingName": "AADB2C_PROVIDER_CLIENT_SECRET"
              },
              "openIdConnectConfiguration": {
                "wellKnownOpenIdConfiguration": "https://XXX.b2clogin.com/XXX.onmicrosoft.com/B2C_1_ms-identity-b2c-javascript-spa_signin/v2.0/.well-known/openid-configuration"
              }
            },
            "login": {
              "nameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
              "scopes": ["openid", "profile"],
              "loginParameterNames": []
            }
          }
        }
      }
    }
  }

DisplayName is requested in User Flow, App Registration set up with SPA and everything but still, code_challenge is missing.

How can I solve that issue? It currently feels I'm pretty close to the solution but yet so far away :)

I followed this whole tutorial: https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-authentication-in-azure-static-app

rubenaster commented 1 year ago

PS: I also switched the platform type from SPA to Web for now - which works.

jmp601 commented 1 year ago

If you register an application as "Web" instead of "SPA", does it support PKCE?

aaltotsky commented 1 year ago

I have the same problem. We are using the React app. I see no solution and the issue is still open.

jacobpoldenzuhlke commented 1 year ago

I had to grant admin access in B2C to the scopes which seemed to solve this problem - in my bash script, I have the following snippet:

echo 'https://login.microsoftonline.com/'"$tenant_id"'/adminconsent?client_id='"$app_id"''

aaltotsky commented 1 year ago

I found a solution: https://github.com/Azure-Samples/ms-identity-javascript-react-tutorial/tree/main/3-Authorization-II/2-call-api-b2c I do not have to use the "static-web-apps" built-in integration.

Penberthy-gossan commented 8 months ago

I have this exact problem its definitely the fact the challenge parameters are missing as you can modify the url of the "sorry page" to add the following and it shows the login page. What do i need to do to get the SWA to supply the PKCE? &code_challenge_method=plain &code_challenge=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

sethdobbins commented 8 months ago

I created a Node.js app following the instructions at https://learn.microsoft.com/en-us/azure/active-directory-b2c/configure-a-sample-node-web-app. As others have mentioned, I needed to add a Web (not SPA) Platform to the Azure AD B2C App Client Registration with the http://localhost:3000/redirect Redirect URI in order to stop getting getting the "AADB2C99059: The supplied request must present a code_challenge" error. The Node.js app uses msal-node. However, React uses msal-browser and msal-react. msal-browser has the initializeAuthorizationCodeRequest method available which sets codeChallenge and codeChallengeMethod.

protected async initializeAuthorizationCodeRequest(request: AuthorizationUrlRequest): Promise { this.logger.verbose("initializeAuthorizationRequest called", request.correlationId); const generatedPkceParams = await this.browserCrypto.generatePkceCodes();

    const authCodeRequest: CommonAuthorizationCodeRequest = {
        ...request,
        redirectUri: request.redirectUri,
        code: "",
        codeVerifier: generatedPkceParams.verifier
    };

    request.codeChallenge = generatedPkceParams.challenge;
    request.codeChallengeMethod = Constants.S256_CODE_CHALLENGE_METHOD;

    return authCodeRequest;
}