How to change msal authority in runtime? - angular7

Background:
I have an angular 7 app.
The app authenticates using Azure Active Directory B2C and Msal for angular
https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular
I have created 2 user flow's in AAD B2C:
Signin flow V2
Signup flow V2
both have UI customized,
both contain a redirect button to the other AAD B2C flow.
(from register -> login | from login -> register)
I have setup MSAL module in angular, with Signin flow as the default authority
e.g
MsalModule.forRoot({
...,
authority: <"https://mytenant.b2clogin.com/tfp/mytenant.onmicrosoft.com/B2C_1_Signin">,
...
})
(notice B2C_1_Signin flow name)
This is working well, user can login and register (navigate between flows from within a flow and authenticate).
Problem:
The problem start when I try to manually change the MSAL module authority
(that was defined in MSAL init as B2C_1_Signin flow when app starts).
Example:
User get to a welcome page and can click Login / Register.
If the user clicks login,all is well because the authority link was defined at start as Signin flow (B2C_1_Signin).
If the user would like to register,
I need to change the authority link
to the selected AAD flow (B2C_1_Signup) and then I call MSAL loginRedirect(), user get redirects to right AAD flow, enter his details and redirect back to the app.
Then the app will start a new login flow and redirects to the login flow as if the user is not authenticated.
This only happens when I change the authority link manually (I have no other choice in order to navigate to right flow).
Otherwise, all is working as expected.
How should I change the authority, so the app will not fail the user authentication?
I have tried using default AAD Signup v1 flow with no customization.
I have tried to reveres the process in order to isolate the problem (setup Signup flow as default,
change authority manually to Signin flow, same error (as expected).
I have searched many different sites and forums but none have the same issue or a fix.
https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/498
https://medium.com/#sambowenhughes/configuring-your-angular-6-application-to-use-microsoft-b2c-authentication-99a9ff1403b3
and many more...
MSAL for angular setup:
"authentication.module.ts"
MsalModule.forRoot({
clientID: "<my-client-id>",
authority: "https://tenant.b2clogin.com/tfp/tenant.onmicrosoft.com/B2C_1_Signin",
redirectUri: "http://localhost:4200/dashboard",
/* default is true */
validateAuthority: false,
/* Defaults is 'sessionStorage' */
cacheLocation : "localStorage",
/* Defaults is 'redirectUri */
postLogoutRedirectUri: "http://localhost:4200/",
/* Ability to turn off default navigation to start page after login. Default is true. */
navigateToLoginRequestUrl : false,
/* Show login popup or redirect. Default:Redirect */
popUp: false,
})
AuthService wrapping MSAL auth service:
"auth.service.ts"
constructor(private msalService: MsalService) {}
public login(scopes: string[] = null, queryParams: string = null): void {
this.msalService.authority = this.tenantConfig.baseURL + "/" + this.tenantConfig.tenant + "/" + this.tenantConfig.signInPolicy;
this.msalService.loginRedirect(scopes, queryParams);
}
public signup(scopes: string[] = null, queryParams: string = null): void {
this.msalService.authority = this.tenantConfig.baseURL + "/" + this.tenantConfig.tenant + "/" + this.tenantConfig.signUpPolicy;
this.msalService.loginRedirect(scopes, queryParams);
}
I expect the Msal module to work as usual even if I change the initial defined authority.

Have you tried submitting an issue against the MSAL GitHub library? The issues section for MSAL.JS can be found here : https://github.com/AzureAD/microsoft-authentication-library-for-js/issues
There's actually an ongoing issue on trying to change authority during runtime per the issue here : https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/784
As it's not exactly the same, I suggest filing a new github issue. However it looks like this is not a supported feature as of yet.

We had a similar problem with our application. The fix was essentially turning off MFA.
If MFA is turned on for sign-in, switching from sign-up to sign-in wasn't giving us an MFA prompt and unceremoniously did not authorize the user to be signed in.
Our solution was to detect if the sign in was attempting directly after a sign up and have a non-MFA sign in flow for that case specifically. Not sure if you're doing MFA, but if you are it's worth a look.

Related

Ionic 5 msal Authentication failed

I am trying to implement Microsoft SSO authentication in ionic 5.
Undefine occurs in the console when the code below is executed. Which part is the problem?
login(){
let authContext: AuthenticationContext = this.msAdal.createAuthenticationContext('https://login.windows.net/common');
authContext.acquireTokenAsync('https://graph.windows.net', 'clientID', 'http://localhost:8200')
.then((authResponse: AuthenticationResult) => {
console.log('Token is' , authResponse.accessToken);
console.log('Token will expire on', authResponse.expiresOn);
})
.catch((e: any) => console.log('Authentication failed', e));
}
error =>
https://ionicframework.com/docs/v3/native/ms-adal/
const browser = this_.iab.create("https://login.microsoftonline.com/" + tenant_id +
"/oauth2/authorize?response_type=id_token&client_id=" + client_id
+"&state=SomeState&nonce=SomeNonce"
, '_blank', 'clearsessioncache=yes,clearcache=yes,toolbar=no,location=no,toolbarposition=top,hidenavigationbuttons=yes,toolbarcolor=#0072bb')
browser.on("loadstart").subscribe(event => {
IAB is inappbrowser. Msal we had also tried to use when we needed this type of authentication for azure AD b2c. Similarly you have to do for your case.
There is enterprise level plugin available which cost around 8 lakhs INR.
So this is the work around. Tenant id and client id you will get when you register your app under applications in azure account.
So once the login is successful that load listener will listen to pages which IAB is visiting. And if found occurrence of #token_id(after sucesfull login).
Automatically the iab will close and ionic app page will resume or you can put some new route entry in that event listener.
May be for your case the base url might change. So try first visiting chrome with the url which opens your case login. And accordingly change.
MAKE IT WORK USING IAB AND NOT MSAL

Openid - Is there a way to get the state & nonce generated by Challenge method without redirection?

I have a requirement to integrate with an external authentication provider which they require us to generate the state & nonce and using these parameters as an input for the embedded JS to generate the QR code, this QR code will be scanned by mobile for authentication.
for the standard login with external authentication provider, we call the Challenge() method to redirect to the login page, and the redirect url contains the state & nonce itself, is there a way to generate/get them without redirection?
If I generate random nonce & state at frontend side then scanning the QR code and completed the authentication in mobile, it returned the authentication code and threw "unable to unprotect the message.State." exception at the IS4, I tried to disable the state validation but it does not work.
configureOptions.ProtocolValidator = new OpenIdConnectProtocolValidator()
{
RequireState = false,
RequireStateValidation = false,
};
Any help would be much appreciated.

How to apply SSO with an existing login screen and a trusted second website

We use IdentityServer3 (IdSvr3) for authorization/authentication. We want to offer the ability for our end user (or resource owner: RO) to log in (log through) to a second trusted website (site B) without login in to site B also, after they have already logged in to an initial website (site A). Site B administers a different set of resources for the RO. It is important that the RO is not redirected to the IdSvr3 login/consent screen. A possible approach I found so far is: inside site A an access token is created by calling RequestResourceOwnerPasswordAsync (passing username and password plus scope = "openid ..."). This access token is send to site B with which the RO can be authenticated. Site B retrieves the user info by calling the connect/userinfo endpoint. I want to know if this is a correct approach/flow. We assume that the RO will always enter site A first, not site B.
Thanks in advance for taking your time to think with me about this.
what you can do here is to send a authorize request to identity server for Site B Scope and request id_token or reference token. Make sure while sending the authorize request to idsrv you are using prompt=none, this way you will get the access_token without showing a consent to the user again if the user is already logged-in to site A.
Below example is doing the same from a JS file. In Site A you can refer to this script file and execute the script using IIFE.
function getIdentityServerURL() {
var url = global.appSettings.identityServerURL
+ "/connect/authorize?client_id=siteB&response_type=id_token token&redirect_uri="
+ global.appSettings.siteBUrl + "/Main/SsoCallback&scope=siteBscope openid email roles&prompt=none&nonce="
+ genNonce();
return encodeURI(url);
}
The code above will redirect you to SsoCallback page where you can create a virtual iframe and post the token back to site B after reducing the result from authorize request. Refer to code below.
<script type="text/javascript">
var identityresult = window.location.hash.split('&').reduce(function (result, item) {
var parts = item.split('=');
result[parts[0]] = parts[1];
return result;
}, {});
window.parent.postMessage(identityresult, '*');
your script can listen to postmessage event. Hope this helps.

How to force account selection when using social provider login in Lock

I am having trouble clearing the last login when using social providers.
Currently using Lock with the following options:
var options = {
rememberLastLogin: false,
auth: {
sso: false,
redirect: false
}
};
var lock = new Auth0Lock('clientID', 'account.auth0.com', options);
The issue is that when executing the next steps it always logins with the same account:
Call lock.show
Select social login provider (Google)
Attempt login with account A [non authorized account]
Lock shows "You are not allowed to access this application." [expected result]
Click Google button again and it still tries to log into the same account A (Lock does not offer a way to try different login from same social provider)
Close Lock and reopen it
Click Google button and it still uses same login account A (no option to enter new account)
What can I do to be able to select a different account?
When you use a social login provider the automatic sign-in is handled by the provider in question. Disabling the sso option and rememberLastLogin will mean Auth0 will not try to login you automatically or provide any information about who login for the last time.
When you login with Google the first time, Google created a session and next requests will automatically use that session by default.
However, Google supports an option that will allow you to choose the behavior you want, in this case it seems you want for the user to be able to select another account, which can be accomplished by passing the following option prompt=select_account (see other options here) in the Google login request.
You can achieve this in Auth0 Lock by providing this option in the auth.params object. Updated example below:
var options = {
rememberLastLogin: false,
auth: {
sso: false,
redirect: false,
params: { prompt: 'select_account' }
}
};
var lock = new Auth0Lock('clientId', '[tenant].auth0.com', options);
Or if you use Auth0.WebAuth then you need to pass param to authorization call:
auth0Instance.web.authorize({
prompt: 'select_account'
})

What is the best way to dynamically specify the redirect url for OAuth strategies in passport.js?

I have setup my facebook auth per passportjs docs:
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy;
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://www.example.com/facebook/callback"
},
function(accessToken, refreshToken, profile, done) { ... });
}
));
app.get('/login/facebook', passport.authenticate('facebook'))
.get('/facebook/callback', passport.authenticate('facebook', {successRedirect: '/', failureRedirect: '/login'}));
All this works fine. However, there are cases (such as token expiration) when I want to automatically redirect the user to the page that the user was on before initiating the login request. So I tried to plumb a query string param through the login request (from client to server to facebook and back). But I cant see a way to specify that in the callbackURL.
Furthermore, when I tried hard-coding some context param to the config callbackURL (eg: "http://www.example.com/facebook/callback?redir=lastUserPage") I get an OAuth parse error. Interestingly enough, Facebook does respond correctly with the access code as well as the redir param, but it fails with OAUTH exception:
FacebookTokenError: Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request
at Strategy.parseErrorResponse (C:\Sources\node_modules\passport-facebook\lib\strategy.js:198:12)
at Strategy.OAuth2Strategy._createOAuthError (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:345:16)
at C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:171:43
at C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:177:18
at passBackControl (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:124:9)
at IncomingMessage.<anonymous> (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:143:7)
at IncomingMessage.emit (events.js:117:20)
at _stream_readable.js:943:16
at process._tickCallback (node.js:419:13)
Note that I had this working using WIF before. I don't see any security concerns with passing additional query string parameters through the OAuth process..
Any idea how I can get past this?
I'm not sure how to do what you're asking, but for your desired end goal you could:
Save a cookie before authenticating
Authenticate the user
on the resulting callback page, check for the cookie and redirect if present.
Wouldn't this work just as easily?

Resources