Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure B2C #91

Closed
fredbjork opened this issue Apr 22, 2020 · 24 comments
Closed

Azure B2C #91

fredbjork opened this issue Apr 22, 2020 · 24 comments

Comments

@fredbjork
Copy link

fredbjork commented Apr 22, 2020

Description

I'm trying to use this plugin with Azure B2C in my Angular project, but haven't been able to make it work.

If anyone here has done this already, some help would be much appreciated.

I get sent to the Azure Policy where I can sign in, but on redirect I get 2 types of errors:

  1. If I set responseType: "code token":
    ERR_GENERAL: See client logs. It might be CORS. Status text:
    "Access to XMLHttpRequest at 'https://{tenant}b2c.b2clogin.com/{tenant}b2c.onmicrosoft.com/oauth2/v2.0/token' from origin 'http://localhost:8100' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."

  2. If I set responseType: "token":
    "OAuth rejected Error: ERR_NO_AUTHORIZATION_CODE"

Capacitor version:

@capacitor/cli 2.0.1
@capacitor/ios 2.0.1
@capacitor/core 2.0.1

Library version:

  • 2.0.0

OAuth Provider:

  • Azure AD
  • Azure App Registration

Your Plugin Configuration

{
    appId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    authorizationBaseUrl: "https://{tenant}b2c.b2clogin.com/{tenant}b2c.onmicrosoft.com/oauth2/v2.0/authorize",
    accessTokenEndpoint: "https://{tenant}b2c.b2clogin.com/{tenant}b2c.onmicrosoft.com/oauth2/v2.0/token",    
//"https://{tenant}b2c.b2clogin.com/{tenant}b2c.onmicrosoft.com/B2C_1_SignUpAndSignIn/oauth2/v2.0/{authorize/token}",
    scope: "openid https://{tenant}b2c.onmicrosoft.com/capacitor-api/demo.read",
    responseType: "code token",
    web: {
        redirectUrl: "http://localhost:8100/auth",
    },
    ios: {
        responseType: "code token",
        redirectUrl: "msalxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx://auth"
    },
    additionalParameters: {
      p: "B2C_1_B2C_1_SignUpAndSignIn",
      response_mode: "query"
    }
}

Thanks!

@NishaBhat
Copy link

Hi,
We have a B2B app and this is the config we used to connect to Microsoft AD
{
authorizationBaseUrl:
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
accessTokenEndpoint: "http://localhost:8100/",
scope:
"User.ReadBasic.All Calendars.Read Calendars.ReadWrite Contacts.Read",
resourceUrl: "http://localhost:8100/",
web: {
appId: "",
responseType: "token",
accessTokenEndpoint: "", // clear the tokenEndpoint as we know that implicit flow gets the accessToken from the authorizationRequest
redirectUrl: "http://localhost:8100/",
windowOptions: "height=600,left=0,top=0",
},
android: {
appId: "",
responseType: "code", // if you configured a android app in google dev console the value must be "code"
redirectUrl:
"", // package name from google dev console
},
ios: {
appId: "",
responseType: "code", // if you configured a ios app in google dev console the value must be "code"
redirectUrl: "com.companyname.appname:/", // Bundle ID from google dev console
},
};

@fredbjork
Copy link
Author

Thanks, it works on the web when I clear the accessTokenEndpoint.

I now have the following config:

appId: "xxxxxx-xxxx-xxxxx-xxx-xxxxxxxx",
authorizationBaseUrl: "https://tenant.b2clogin.com/tfp/tenantb2c.onmicrosoft.com/B2C_1_SignUpAndSignIn/oauth2/v2.0/authorize",
accessTokenEndpoint: "",
scope: "openid offline_access https://tenantb2c.onmicrosoft.com/capacitor-api/demo.read",
responseType: "token",
web: {
        redirectUrl: "http://localhost:8100/auth",
},
android: {
        redirectUrl: "msalxxxxxxx-xxxxx-xxx-xxxx-xxxxxxxx://auth"   
},
ios: {
        redirectUrl: "msauth.com.xxxxx://auth",
        resourceUrl: "https://graph.microsoft.com/"
}

I now get the following errors when redirected back to the apps:

Android:
D/Capacitor: Sending plugin error: {"save":false,"callbackId":"53281380","pluginId":"OAuth2Client","methodName":"authenticate","success":false,"error":{"message":"ERR_ANDROID_NO_INTENT"}}
D/Capacitor: App restarted
D/Capacitor: App started
V/Capacitor/Plugin/App: Notifying listeners for event appUrlOpen
D/Capacitor/Plugin/App: No listeners found for event appUrlOpen
D/Capacitor/Plugin/App: Firing change: true
V/Capacitor/Plugin/App: Notifying listeners for event appStateChange
D/Capacitor/Plugin/App: No listeners found for event appStateChange
D/Capacitor: App resumed
D/EGL_emulation: eglMakeCurrent: 0xe5646560: ver 3 0 (tinfo 0xc618b9d0)
D/eglCodecCommon: setVertexArrayObject: set vao to 1 (1) 0 4
D/eglCodecCommon: setVertexArrayObject: set vao to 0 (0) 9 8
E/Capacitor/Console: File: http://localhost/main-es2015.js - Line 1029 - Msg: OAuth rejected
V/Capacitor/Plugin/Network: Notifying listeners for event networkStatusChange
D/Capacitor/Plugin/Network: No listeners found for event networkStatusChange

iOS:
@byteowls/capacitor-oauth2: Invalid json in resource response The data couldn’t be read because it isn’t in the correct format..
ERROR MESSAGE: {"errorMessage":"","message":"ERR_GENERAL"}
⚡️ [error] - OAuth rejected {"errorMessage":"","message":"ERR_GENERAL","line":224,"column":23,"sourceURL":"user-script:2"}

In string.xml:
<string name="custom_url_scheme">msalxxxx-xxxxx-xxxx-xxx-xxxxxxx</string>
AppManifest:
<data android:scheme="@string/custom_url_scheme" android:host="auth">

info.plist:
<string>msauth.com.xxxxxxx</string>

Does anyone have any idea what I should change?

@NishaBhat
Copy link

NishaBhat commented Apr 24, 2020

A colleague of mine got IOS working. I haven't got Android working yet but try this config for IOS
ios: {
redirectUrl: "msauth.com.xxxxx://auth",
resourceUrl: "https://graph.microsoft.com/"
pkceEnabled: true,
responseType: "code",
accessTokenEndpoint:
"https://login.microsoftonline.com/xxx/oauth2/v2.0/token",
}
PKCE is used for encryption and is ideally needed both for web and native apps. We don't have resourceUrl though and it worked. There was also I change for xcode plist, I will find out and keep yu posted.
Let me know if you make any progress with android

@fredbjork
Copy link
Author

It worked on ios with your config, thanks.

Still the same error on Android. I'll let you know if I figure it out.

@moberwasserlechner
Copy link
Collaborator

  1. Maybe try those Android specific options, they were introduced because of Android: App crashes if Intent data is null #52 and Android crash with NullPointerException #55.
{
   ...
   android: {
      handleResultOnNewIntent = false
      handleResultOnActivityResult = true
   }
}

You have to play with those options. I could not reproduce the issue myself but added these params so developers have some options to work around it.

  1. See Android setup: https://github.com/moberwasserlechner/capacitor-oauth2#platform-android

  2. Which Android versions and devices did you use for testing?

@fredbjork
Copy link
Author

fredbjork commented Apr 27, 2020

I made it work on Android as well. I created a new Custom redirect URI on my b2c app with the following structure: "com.tenant.app://oauth/auth".

Then added "handleResultOnNewIntent: true" in the config.

@NishaBhat
Copy link

NishaBhat commented Apr 27, 2020

Thanks, I have used pixel 2 Android version 9 & 10 for testing
In AndroidManifest.xml, if I have

<intent-filter  >
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="msauth"
                    android:host="com.xxx" />
</intent-filter>

The control gets redirected to my app but Plugins.OAuth2Client.authenticate(this.oauth2Options) callback is not hit. If I use
<data android:scheme="oauth" android:host="com.xxx" />
the control does not come back to my app

oauth2Options: OAuth2AuthenticateOptions = {
    appId: "xxx",
    authorizationBaseUrl:
      "https://login.microsoftonline.com/xxx/oauth2/v2.0/authorize",
    responseType: "code",
    redirectUrl: "http://localhost:8100",
    scope:
      "openid User.ReadBasic.All Calendars.Read Calendars.ReadWrite Contacts.Read",
    accessTokenEndpoint:
      "https://login.microsoftonline.com/xxx/oauth2/v2.0/token",
    logoutUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/logout",
    pkceEnabled: true,
    web: {
      redirectUrl: "http://localhost:8100",
      accessTokenEndpoint: "",
      responseType: "token",
    },
    android: {
      responseType: "token",
      redirectUrl:
        "msauth://xxx",
      resourceUrl: "https://graph.microsoft.com/",
      handleResultOnNewIntent: true,
      handleResultOnActivityResult: false,
    },
    ios: {
      redirectUrl: "xxx",
    },
  };

is my configuration

@fredbjork
Copy link
Author

Try switching your scheme and host.

<data android:scheme="com.xxx" android:host="oauth" />

@moberwasserlechner
Copy link
Collaborator

Thanks for helping each other. I guess this task is now ready to be closed?

If you could be so nice to post a full config for Azure B2C I'll add it as a example to the readme for others having the same provider.

@moberwasserlechner moberwasserlechner added the needs-reply The author needs to reply to questions. Task will be closed after 5days if not answered. label May 1, 2020
@tekagesolutions
Copy link

tekagesolutions commented May 5, 2020

Almost ready to be closed...so I've followed the steps outlined by these amazing developers in this thread and I finally get the popup, signup/signin and all is wonderful....until.....I either finish signing up or finish signing in and I'm getting the following in my console:
OAuth rejected Error: ERR_NO_ACCESS_TOKEN

It never appears to hit the "then" which I have starting with a log:
).then(response => { console.log("AccessToken: " + response["access_token"]); console.log("RefreshToken: " + response["refresh_token"]); let accessToken = response["access_token"]; this.refreshToken = response["refresh_token"];

but goes right to the catch. What am I missing? Here's my config:
{ authorizationBaseUrl: "https://{{tenant}}.b2clogin.com/{{tenant}}.onmicrosoft.com/B2C_1_csa_signupsignin/oauth2/v2.0/authorize", accessTokenEndpoint: "http://localhost:8100/", scope: "openid offline_access", resourceUrl: "http://localhost:8100/", web: { appId: "{{appId}}", responseType: "token", accessTokenEndpoint: "", // clear the tokenEndpoint as we know that implicit flow gets the accessToken from the authorizationRequest redirectUrl: "http://localhost:8100/home", windowOptions: "height=600,left=0,top=0", }

@fredbjork
Copy link
Author

Make sure your Azure App Config is setup correctly with allowImplicitFlow: true and Grant admin consent for your permissions.

Here's my config for B2C:

    appId: "xxxxxxxxx",
    authorizationBaseUrl: "https://tenantb2c.b2clogin.com/tfp/tenantb2c.onmicrosoft.com/B2C_1_SignUpAndSignIn/oauth2/v2.0/authorize",
    accessTokenEndpoint: "",
    scope: "openid offline_access https://tenantb2c.onmicrosoft.com/capacitor-api/demo.read",
    responseType: "token",
    web: {
        redirectUrl: "http://localhost:8100/auth"
    },
    android: {
        pkceEnabled: true,
        responseType: "code",
        redirectUrl: "com.tenant.app://oauth/auth",
        accessTokenEndpoint: "https://tenantb2c.b2clogin.com/tfp/tenantb2c.onmicrosoft.com/B2C_1_SignUpAndSignIn/oauth2/v2.0/token",
        handleResultOnNewIntent: true,
        handleResultOnActivityResult: true
    },
    ios: {
        pkceEnabled: true,
        responseType: "code",
        redirectUrl: "msauth.com.tenant://oauth",
        accessTokenEndpoint: "https://tenantb2c.b2clogin.com/tfp/tenantb2c.onmicrosoft.com/B2C_1_SignUpAndSignIn/oauth2/v2.0/token",
    }

@tekagesolutions
Copy link

@fredbjork - Thanks for replying. It looks as though I have the allow implicit flow set, but unsure about the Grant Admin consent would be located on the Azure ADB2C blade. I'm assuming that is what's tied to the last scope item you have on your reply?

@fredbjork
Copy link
Author

Yes, go to App Reg. (preview) > YourApp > API permissions > Add your permissions and "Grant...".
Pop

@tekagesolutions
Copy link

@fredbjork - ok I got it, after granting those permissions I was still getting an error. Turns out if I had paid closer attention to your config, I would have noticed you didn't have a few of the items I had in mine. Once I removed those items and made it match yours, it worked like a charm. Thanks again!!

@moberwasserlechner moberwasserlechner removed the needs-reply The author needs to reply to questions. Task will be closed after 5days if not answered. label May 6, 2020
@moberwasserlechner
Copy link
Collaborator

I added the config to the README. @fredbjork thx for sharing.

@loonix
Copy link

loonix commented May 14, 2020

@fredbjork did you face the issue I have posted #96 ?
Are you able to share a sample of azure b2c working on android and ios? that would be amazing if you could, struggling to make mine to work.

@billyjacobs2014
Copy link

billyjacobs2014 commented May 19, 2021

@fredbjork

You said above:

I made it work on Android as well. I created a new Custom redirect URI on my b2c app with the following structure: "com.tenant.app://oauth/auth".

Then added "handleResultOnNewIntent: true" in the config.

How did you add this custom redirect URI. When I add a new URI for the Android Platform, it is locked down. The redirect url always begins with msauth://.

@billyjacobs2014
Copy link

billyjacobs2014 commented May 23, 2021

@NishaBhat did you ever get your android version working. I am having the same issue that you described. My config is identical to what you have with the msauth:// and I am getting the exact same behavior you describe where control returns to my app but callback is not hit because of the ERR_ANDROID_NO_INTENT. @fredbjork stated that he solved it by adding a custom redirect URI on his b2c app as I mentioned just above, but I see no way of adding a custom redirect url as he describes. I do have it working on ios. I have tried all of the suggestions in this chain but still can't get it to work.

@moberwasserlechner does the plugin handle the msauth:// android redirectUrl format?

In the azure portal for android the RedirectUrl has to be in the following format:
msauth://com.mydomain.myapp/SIGNATURE_HASH

So in my config it has:
android: { pkceEnabled: true, responseType: "code", redirectUrl: "msauth://com.mydomain.myapp/SIGNATURE_HASH_ENCODED", // THIS IS THE VALUE FROM AZURE accessTokenEndpoint:${this.appSettings.GetMsalTenantAuthorityUri()}/oauth2/v2.0/token, handleResultOnNewIntent: true, handleResultOnActivityResult: false },

And in my AndroidManifest.xml I have:
<data android:scheme="msauth" android:host="com.mydomain.myapp" android:path="/SIGNATURE_HASH_NO_ENCODE" />

Again I get the same exact bahavior as @NishaBhat described.

Thanks,

@NishaBhat
Copy link

NishaBhat commented May 24, 2021

Hi Billy,
Yes I did get Android working. Let me share my config with you. In the azure portal, for android, the RedirectUrl has to be in the following format:
Package name: com.mydomain.myapp
Signature: <SIGNATURE_HASH>
Redirect URI: msauth://<Package_name>/<SIGNATURE_HASH>
Looks like you have this configured correctly in Azure.But change your redirect URL to com.mydomain.myapp://oauth/auth.
This is my JSON configuration

{
                    "appId" : "<APPID>",
                    "authorizationBaseUrl" : "https://login.microsoftonline.com/<domain>/oauth2/v2.0/authorize",
                    "scope" : "openid profile ...",
                    "accessTokenEndpoint" : "https://login.microsoftonline.com/<domain>/oauth2/v2.0/token",
                    "pkceEnabled" : true,
                    "additionalParameters" : {
                        "nonce" : "1"
                    },
                    "web" : {
                        "redirectUrl" : "https://<REDIRECT_URL>/",
                        "accessTokenEndpoint" : "",
                        "responseType" : "token id_token",
                        "windowTarget" : "_self"
                    },
                    "android" : {
                        "responseType" : "code",
                        "redirectUrl" : "com.mydomain.myapp://oauth/auth",
                        "handleResultOnNewIntent" : true,
                        "handleResultOnActivityResult" : true
                    },
                    "ios" : {
                        "responseType" : "code",
                        "redirectUrl" : "msauth.com.mydomain.myapp://auth"
                    }
                }

@billyjacobs2014
Copy link

billyjacobs2014 commented May 24, 2021

Hi @NishaBhat, I changed my redirect uri as you suggested in the android config section but no matter what, when I run the app I get the error in the screen shot below.

Couple of questions:

  1. In Azure, do you have yours redirectUrl as the following: msauth://com.domain.app/SIGNATURE_HASH?

  2. In Android config (above) do you have your redirectUrl set to: "redirectUrl" : "com.domain.app://oauth/auth"?

  3. In AndroidManifest.xml what do you have there? I have:
    <data android:scheme="msauth" android:host="com.domain.app" android:path="/SIGNATURE_HASH" />
    I also tried
    <data android:scheme="msauth" android:host="com.domain.app" />
    and
    <data android:scheme="com.domain.app" android:host="oauth" />

  4. What do you have in your build.gradle file for the manifestPlaceholder?
    I have: manifestPlaceholders = ["appAuthRedirectScheme": "com.domain.app"]

Do you see anything I have misconfigured? I don't see how I can get past the error below using the redirectUrl you suggested, since it is not registered that way in Azure.

Thanks, I really appreciate the help.

image

@NishaBhat
Copy link

NishaBhat commented May 24, 2021

Hmm, I might know what's wrong. Have you added
com.domain.app://oauth/auth to
Mobile and desktop applications -> Redirect URIs in Azure Portal?

Yes, I do have msauth://com.domain.app/SIGNATURE_HASH as redirect_url in Azure

In AndroidManifest.xml I have

<intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="com.domain.app" android:host="foo" />
                </intent-filter>

In build.gradel I have
android.defaultConfig.manifestPlaceholders = [
appAuthRedirectScheme:"com.domain.app"
]

@billyjacobs2014
Copy link

billyjacobs2014 commented May 24, 2021

Hi @NishaBhat

I really appreciate your help. I think I am close.

I did what you said and setup the com.domain.app://oauth/auth in azure where you said. That did partially work. I now get to the login with no problems and then once I login I am redirected back to my android app. However I get the following error:
ERR_ANDROID_NO_INTENT. My AndroidManifest.xml has

<intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="com.domain.app" android:host="oauth"  />
            </intent-filter>

Here is my entire capacitor-oauth2 config:

{
      appId: this.appSettings.GetMsalClientId(),
      authorizationBaseUrl: `${this.appSettings.GetMsalTenantAuthorityUri()}/oauth2/v2.0/authorize`,
      accessTokenEndpoint: "",
      scope: `${this.appSettings.GetMsalClientScope()} offline_access`, 
      responseType: "token",
      web: {
          redirectUrl: this.appSettings.GetLoginRedirectUri()
      },
      android: {
          pkceEnabled: true,
          responseType: "code",
          redirectUrl: "com.domain.app://oauth/auth",
          accessTokenEndpoint: `${this.appSettings.GetMsalTenantAuthorityUri()}/oauth2/v2.0/token`,
          handleResultOnNewIntent: true,
          handleResultOnActivityResult: true
      },
      ios: {
          pkceEnabled: true,
          responseType: "code",
          redirectUrl: "msauth.com.domain.app://auth",
          accessTokenEndpoint: `${this.appSettings.GetMsalTenantAuthorityUri()}/oauth2/v2.0/token`,
      }
    }

I debugged the android code to see what is happening and this is what I found:

The handleAuthorizationRequestActivity gets call 2 times because I have handleResultOnNewIntent and handleResultOnActivityResult set to true.

The first time handleAuthorizationRequestActivity is called, the intent is null, which I think is ok according to the comments. This causes it to call savedCall.reject(ERR_ANDROID_RESULT_NULL) which is defined as ERR_ANDROID_NO_INTENT.

The second time handleAuthorizationRequestActivity is called, the intent has a value. It then tries to create an AuthorizationResponse from the intent by checking to see if the extras on the intent has an extra with the following key: net.openid.appauth.AuthorizationResponse defined by the constant EXTRA_RESPONSE. An Extra with that key is not found so it returns null. Next it checks to see if there is an error on the Intent by checking to see if there is an Extra with the key: net.openid.appauth.AuthorizationException defined by EXTRA_EXCEPTION. The extra does not exist so it returns null for the error.

Since both of these are null, when it next calls this.authState.update(authorizationResponse, error) an exception is thrown stating that one of them should be non-null. Finally it calls savedCall.reject(ERR_GENERAL, e) and control is returned back to the Android app with error(s).

In the dev console I see the 2 errors. The first error, I assume, is from when the intent is null on the first call to handleAuthorizationRequestActivity. The error is ERR_ANDROID_NO_INTENT which seems to be a handled exception and the second which is an unhandled exception ERR_GENERAL created when it could not find the authorizationresult and no error in the extras of the intent.

@CarlosDez23
Copy link

@billyjacobs2014 Hey could you fix the problem with the Intent, I am really stuck at this point. I debug the app and I see what you told. Did you find a solution?
Thank you :)

@VignesViji
Copy link

VignesViji commented Dec 20, 2022

HI @NishaBhat @billyjacobs2014 @moberwasserlechner @CarlosDez23 "The control gets redirected to my app but Plugins.OAuth2Client.authenticate(this.oauth2Options) callback is not hit" Facing the same issue. could you guys help me fix it. TIA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants