Cannot Authenticate SPA with passport-azure-ad's Bear Strategy - angularjs

So I was playing get Integrating Azure AD into an AngularJS single page app
this tutorial to work.
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-devquickstarts-angular-node
It is implemented for the Azure AD v2.0 endpoint, but unfortunately my organization does not support the Azure AD v2.0 endpoint, therefore I replaced the experimental version of adal.js and adal-angular libraries with GA adal.js and adal-angularlibraries. The front-end authentication works perfect. However after I modified the back-end config based on passport-azure-ad. The back-end failed to authenticate the tokens that was implicitly granted.
I have set the allow implicit flow to true in the manifest file. Also I tried to use my client ID and Tenant Name in another example that uses the same angular front end but .NET as the back-end. It worked!
Here is my back-end config
exports.creds = {
// The app id you get from the registration portal
audience: 'http://localhost:8080',
clientID: '**********************************',
// Passport will use this URL to fetch the token validation information from Azure AD
identityMetadata: '************************************',
// Required.
// If you are using the common endpoint, you should either set `validateIssuer` to false, or provide a value for `issuer`.
validateIssuer: true,
// Required.
// Set to true if you use `function(req, token, done)` as the verify callback.
// Set to false if you use `function(req, token)` as the verify callback.
passReqToCallback: false,
// Required if you are using common endpoint and setting `validateIssuer` to true.
// For tenant-specific endpoint, this field is optional, we will use the issuer from the metadata by default.
issuer: '**************************************',
isB2C: false,
// Optional. Default value is false.
// Set to true if you accept access_token whose `aud` claim contains multiple values.
allowMultiAudiencesInToken: false,
// Optional. 'error', 'warn' or 'info'
loggingLevel: 'info'
};
My server:
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// Pull in the Azure AD bearer passport strategy
var OIDCBearerStrategy = require('passport-azure-ad').BearerStrategy;
// This object is used for in-memory data storage, instead of a database.
// Each time you run the server, you will get a fresh, empty list.
var tasks = [];
// Load passport and configure it to use Azure AD Bearer auth
app.use(passport.initialize());
passport.use(new OIDCBearerStrategy({
identityMetadata: config.creds.identityMetadata,
audience: config.creds.audience,
clientID: config.creds.clientID,
validateIssuer: true,
issuer: config.creds.issuer,
}, function (token, done) {
console.log("yooo");
return done(null, token, null);
}));
var router = express.Router();
router.route('/api/tasks')
.post(passport.authenticate('oauth-bearer', { session: false }), controller)
Here is the output from my browser console after the front-end authenticated:
State: **************
adal.js:973 State status:true
adal.js:973 State is right
Has anybody done similar things?

As mentioned in the comment above by the Author of this question, the audience value in the config should be the client ID.

If anybody have trouble getting it started with Angularjs1 + Nodejs + adal.js v1.x
Here is an working example. Just change the values in the config.js and app.js
https://github.com/lightertu/Azure-ActiveDirectory-V1-Angularjs-Nodejs-SPA-Sample

Related

Azure Authentication - Access Token returning wrong AUD(00000003-0000-0000-c000-000000000000)

I'm trying to authenticate with API Management in Azure through OAuth. I've set up that piece fine.
However from the response, the aud(00000003-0000-0000-c000-000000000000) is invalid from the access token.
Any suggestions/ideas to get the accurate aud in access_token.
I tried to reproduce the same in my environment and got the results like below:
I generated the access token with the same aud as you and got the validation error:
I agree with juunas, To authenticate with API Management in Azure through OAuth, make sure to pass the scope while generating the access token.
I created an Azure AD Application, exposed an API and added scope like below:
Added API permissions like below:
To resolve the error, make sure to pass scope as api://AppID/.default.
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:api://ee1782a6-a994-4013-a396-XXXXX/.default
grant_type:client_credentials
A valid access token to access APIM will be generated like below:
To pass the particular scope from react app using MSAL you can make refer the below sample code:
auth: {
authority: "https://login.microsoftonline.com/common",
clientId: "ClientID",
postLogoutRedirectUri: RedirectURI
redirectUri: RedirectURI
validateAuthority: true,
navigateToLoginRequestUrl: true,
},
cache:
{ cacheLocation: 'sessionStorage',
storeAuthStateInCookie: true,
},
},
{
scopes: ['api://clientid/.default']
},
LoginType.Redirect
References:
OAuth 2.0 Authorisation with the Client Credentials Flow on Azure API Management by Paco de la Cruz
Connect React App with Azure AD using react msal by Ray
You have mistaken the values.
TL;DR: ignore "access token", obtain and read "id token" and verify that "aud" field is your client ID.
First you might obtain a single-use access code (likely something like 0.ABC). Optionally you could fetch open id token. "scope" must include "openid"
Then you can fetch actual open id token using the single-use code. "scope" must be "openid" again. Response might include:
access token - which can be anything including random number of characters, string, your full details or an JWT; I believe that Microsoft returns JWT which is meant to the "00000003-0000-0000-c000-000000000000" audience (meaning "only 00000003-0000-0000-c000-000000000000 can use it - ignore if you are NOT the one")
id token - which is an JWT and should contain your application ID (client ID) in the "aud" field
Always check the "aud" as this says who is the token created for. If it is not you - the token is not for you.

React Native, unable to get access token

I'm using react-native-app-auth to get access token from azure ad b2c but I'm facing issues in android and in IOS
in android it's showing error that
Unable to complete authorization as there is no interactive call in progress. This can be due to closing the app while the authorization was in process.
https://i.stack.imgur.com/R2Fft.png
and in IOS it's redirecting but not returning anything.
that's my code
const config = {
issuer:
'https://ksg1806.b2clogin.com/tenantId/v2.0/',
clientId: 'clientId',
redirectUrl:
Platform.OS == 'ios'
? 'msauth.com.gfk.consumervoice://auth'
: 'msauth://com.gfkconsumer/Cb7s2L1nogp57%2BKdddohtF8%2Funk%3D',
additionalParameters: {},
scopes: ['openid'],
serviceConfiguration: {
authorizationEndpoint:
'https://ksg1806.b2clogin.com/ksg1806.onmicrosoft.com/b2c_1_signup_signin/oauth2/v2.0/authorize',
tokenEndpoint:
'https://gfkms.b2clogin.com/GFKMS.onmicrosoft.com/b2c_1_signup_signin/oauth2/v2.0/token',
revocationEndpoint:
'https://gfkms.b2clogin.com/GFKMS.onmicrosoft.com/b2c_1_signup_signin/oauth2/v2.0/logout',
},
};
const getAccessToken = async () => {
try {
const authState = await authorize(config);
console.log(authState);
} catch (error: any) {
Alert.alert('Failed to log in', error.message);
}
};
if anyone know about that please help I'm stuck here for many days
Please check if below points can narrow down the issue:
For v2 endpoint scope acts as resource . If the resource identifier
is omitted in the scope parameter, the resource is assumed to be
Microsoft Graph. If token is for Microsoft graph try by changing or
adding scope : User.Read . For example, scope=User.Read is
equivalent to https://graph.microsoft.com/User.Read and
offline_access is for refresh tokens Or try to Add the scope to
existing openid scope like scopes: []
For example:
scopes: ['openid', 'profile', email,'offline_access'], (or) scopes: ['openid', 'profile','offline_access']
Optional:Try also by giving client_secret inside the config
Make sure you have set the redirect url correctly.Add a trailing slash to redirect url to your config - e.g. msauth.BUNDLEID://auth/ - failure to add that may cause it to fail in IOS.
The cause may also be due to the user cancelling the operation in the middle of the process before authentication is completed.See Troubleshooting B2C | Microsoft Docs
References:
AppAuth iOS Token Exchange Problems Azure AD - Stack Overflow
Microsoft identity platform scopes, permissions, & consent | Microsoft Docs
react-native-app-auth (github.com)

react-aad-masal token renewal issue

In one of my react project, I am using react-aad-msal for the authentication. But, it seems that I am having the following issue:
First time try to login and it works
After one hour the token expires and can't access the app
Need to clear the cache/browser history and re-run the react app again to access the page
Does anybody have experienced the similar issues. Here is the authProvider.js file looks like:
const config = {
auth: {
authority: `https://login.microsoftonline.com/${authority}`,
clientId,
validateAuthority: false,
redirectUri: window.location.origin,
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true,
},
};
const authenticationParameters = {
scopes: ['profile', 'offline_access'],
};
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: `${window.location.origin}/auth.html`,
};
Thanks in advance for the help.
• Please check for any conditional access policy that is assigned to your application authentication and sign in mechanism as if this policy is applied to your app’s service principal, your organization, or your application, then it will take precedence over the default values of refresh token timeout and issuance as well as access token also.
Thus, to check the authentication session controls for conditional access in Azure AD, please check the session control settings configured in Security --> conditional access as given below as these settings define the refresh token and session behavior: -
Also, ensure that certain settings in conditional access policy like Sign-in frequency control, persistent browser session control are correctly set or not enabled in your policy according to your requirement. Please find the reference images below for configuration of these settings: -
Please refer the below links for more details: -
https://learn.microsoft.com/en-us/azure/active-directory/conditional-access/howto-conditional-access-session-lifetime
https://learn.microsoft.com/en-us/azure/active-directory/conditional-access/concept-conditional-access-session

What is the best practice to use Oauth2, React, Node.js and Passport.js to authenticate user with Google sign on button?

I want to have a login button in my website so when a user clicks on it, the user can use their Google credentials. I'd like to ideally perform the authentication server side using Express.js and Passport.js.
I implemented authentication server-side but the problem is that I can't make an AJAX request from the website to the server to start authentication because Google or Oauth don't support CORS. So I need to use a href element in my website which would call the server authentication endpoint. However, I can't catch server response in this way.
If I perform the authentication client-side (I'm using React) I could store login state in Redux and allow the user to access the website's resources. However, when the user logs out I need to make sure that server endpoints stop serving the same user which feels like implementing authentication twice: client-side and server-side.
In addition when authenticating client-side, Google opens a popup for the user to authenticate which I think is worse user experience then just a redirect when authenticating server-side.
I'm wondering what the best practice in terms of authenticating using Oauth2/Google. For example, stackoverflow.com also has Google button but just makes a redirect, without any popup, so I guess they figured out a way to perform server-side authentication and to bypass CORS issue.
I faced the same issue. This article is Gold link
1.In auth route File I had following code
const CLIENT_HOME_PAGE_URL = "http://localhost:3000";
// GET /auth/google
// called to authenticate using Google-oauth2.0
router.get('/google', passport.authenticate('google',{scope : ['email','profile']}));
// GET /auth/google/callback
// Callback route (same as from google console)
router.get(
'/google/callback',
passport.authenticate("google", {
successRedirect: CLIENT_HOME_PAGE_URL,
failureRedirect: "/auth/login/failed"
}));
// GET /auth/google/callback
// Rest Point for React to call for user object From google APi
router.get('/login/success', (req,res)=>{
if (req.user) {
res.json({
message : "User Authenticated",
user : req.user
})
}
else res.status(400).json({
message : "User Not Authenticated",
user : null
})
});
2.On React Side After when user click on button which call the above /auth/google api
loginWithGoogle = (ev) => {
ev.preventDefault();
window.open("http://localhost:5000/auth/google", "_self");
}
3.This will redirect to Google authentication screen and redirect to /auth/google/callback which again redirect to react app home page CLIENT_HOME_PAGE_URL
4.On home page call rest end point for user object
(async () => {
const request = await fetch("http://localhost:5000/auth/login/success", {
method: "GET",
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
},
});
const res = await request.json();
//In my case I stored user object in redux store
if(request.status == 200){
//Set User in Store
store.dispatch({
type: LOGIN_USER,
payload : {
user : res.user
}
});
}
})();
5.last thing add cors package and following code in server.js/index.js in node module
// Cors
app.use(
cors({
origin: "http://localhost:3000", // allow to server to accept request from different origin
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true // allow session cookie from browser to pass through
})
);
Your authentication should be done server side. Here is how it works.
You make a fetch or axios call to your authentication route.
Your authentication route sends a request to Google's Authentication servers. This is important to have on the backend because you will need to provide your clientSecret. If you were to store this on the frontend, it would make it really easy for someone to find that value and compromise your website.
Google authenticates the user and then sends you a set of tokens to your callback url to use for that user (refresh, auth, etc...). Then you would use the auth token for any additional authorization until it expires.
Once that expires, you would use the refresh token to get a new authorization token for that client. That is a whole other process though.
Here is an example of what that looks like with Passport.js: https://github.com/jaredhanson/passport-google-oauth2
EDIT #1:
Here is an example with comments of the process in use with Facebook, which is the same OAuth codebase:
https://github.com/passport/express-4.x-facebook-example/blob/master/server.js
Redux can really help with achieving this and this follows the same logic as Nick B already explained...
You set up oauth on the server side and provide an endpoint that makes that call
You set up the button on you react frontend and wire that through an action to the endpoint you already setup
The endpoint supplies a token back which you can dispatch via a reducer to the central redux store.
That token can now be used to set a user to authenticated
There you have it.

IdentityServer4 and Azure AD - Getting blank page 404 to login.live after entering credentials

I am trying to setup Azure AD as an external provider for IdentityServer4 in a development environment. Google and Facebook providers already are hooked up successfully.
I'm directed to the login page on login.microsoft.com where I enter my login id and select an MS account, it then directs me to login.live.com as a blank page (404) instead of redirecting back to my IdentityServer instance.
I've tried a bunch of things but have had no luck.
Do I need to enable Enterprise Applications in Azure?
Am I missing something?
IdentityServer URL:
http://localhost:5000
IdentityServer URL:
http://localhost:47740
...
app.UseOpenIdConnectAuthentication(CreateAzureAdOptions(clientId, tenantId));
...
public static OpenIdConnectOptions CreateAzureAdOptions(string clientId, string tenentId)
{
return new OpenIdConnectOptions
{
DisplayName = "Azure Active Directory",
AuthenticationScheme = "Azure",
ClientId = clientId,
Authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", tenentId),
ResponseType = "id_token",
Scope = { "openid" },
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
AutomaticChallenge = true,
AutomaticAuthenticate = true,
RequireHttpsMetadata = false,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
}
};
}
This probably is related to the size of the url when returning from AAD. This is actualy a 404.15 IIS specific status code. If you have a lot of claims returned from your External identity provider (ie a profile image base64) then you will always hit the cap in url length HTTP Error 404.15 - query url too long RSS.
IdenitityServer4 has a topic in the docs that addresses this issue by using the distributed cache underneath. It is located under Sign-in with External Identity Providers - State, URL length, and ISecureDataFormat
Abstract:
Fortunately, IdentityServer provides an implementation of this for you, backed by the IDistributedCache implementation registered in the DI container (e.g. the standard MemoryDistributedCache). To use the IdentityServer provided secure data format implementation, simply call the AddOidcStateDataFormatterCache extension method on the IServiceCollection when configuring DI. If no parameters are passed, then all OpenID Connect handlers configured will use the IdentityServer provided secure data format implementation.

Resources