Azure using wrong callback url during implicit flow login - javascript

I'm currently struggling with a weird problem in azure active directory implicit flow oauth authentication. I've implemented a spa webapp using msal.js to login users to their microsoft accont.
The userAgentApplication is executed as shown below:
userAgentApplication = new
Msal.UserAgentApplication(client_id,null,function(errorDes,token,error,tokenType)
{
if(error) {
console.log(JSON.stringify(error));
return;
}
},{ redirectUri: 'https://example.com/app/msalCallback.html' });
When they click login executing the is piece of code:
logInPopup = function() {
var uaa = userAgentApplication;
return new Promise(function(resolve,reject) {
uaa.loginPopup([
'https://graph.microsoft.com/user.read'
]).then(function(token) {
//signin success
console.log(token);
var user = uaa.getUser();
console.log(JSON.stringify(user));
resolve(user);
}, function(error) {
console.log(JSON.stringify(error));
reject(error);
});
})
}
The popup comes up and the user tries to login but the following error comes up:
Microsoft account is experiencing technical problems. Please try again later.
In the url the error parameters string is:
error_description=The provided value for the input parameter
'redirect_uri' is not valid The expected value is
'https://login.live.com/oauth20_desktop.srf' or a URL which matches
the redirect URI registered for this client application.
Upon further research I found that though I configured the redirect uri to be
https://example.com/app/msalCallback.html
(Which I confirmed on the application registration page to be true)
The redirect_uri of the /oauth2/v2.0/authorise url in the login popup page is:
redirect_uri=https://example.com/app/
Which is weird but the above uri is not random one. It is in fact the callback uri for a previous previously registered but now deleted app with the same name.
Further investigation showed that when I config Msal to use the old the redirect_uri login passes.
I'm fresh out of ideas. It looks like a bug in the azure network but wanted to know if anyone else has had this problem or at least point me in the right direction towards getting in contact with azure to find a fix.
Thanks in advance

I've found the cause of the problem after carefully reviewing the msal.js documentation i found that i was setting the redirectUri incorrectly. The correct way is as follows:
var userAgentApplication = new
Msal.UserAgentApplication(client_id,null,function(errorDes,token,error,tokenType)
{
if(error) {
console.log(JSON.stringify(error));
return;
}
});
userAgentApplication.redirectUri = 'https://example.com/app/msalCallback.html'
Hope that helps.
regards

Related

Is it possible to authenticate from SPA with Microsoft Authentication Library but without Redirect Uri?

I'm creating a Javascript single page application. It requires a user to sign in during the implicit flow in order to use Microsoft Graph API later on.
I'm using MSAL.js and trying to adapt snippets from this guide to get auth token from authRedirectCallBack
I realize that in other flows redirect uri (used after authentication) is essential to get the token and proceed with it.
However in my flow I have token processed in the Javascript callback function.
Is it possible to avoid providing redirect uri in App registration with Azure AD and/or during the code execution? I'd like not to bring the user to this redirect uri at all.
At the moment my code looks like this:
var msalConfig = {
auth: {
clientId: 'my-app-id',
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true,
forceRefresh: false
}
};
const loginRequest = {scopes: ['openid', 'profile', 'user.read']};
const msalClient = new Msal.UserAgentApplication(msalConfig);
msalClient.handleRedirectCallback(authRedirectCallBack);
singIn();
async function singIn() {
try {
msalClient.loginPopup(loginRequest).then(function (response) {
if (msalClient.getAccount()) {
console.log('logged');
}
});
} catch (err) {
console.log(err);
}
}
function authRedirectCallBack(err, response) {
if (err) {
console.log(err);
} else {
if (response.tokenType === "access_token") {
console.log('token', response);
}
}
}
A redirect URI is required, as it is the main security mechanism we use to ensure the response is not returned to an unauthorized party, as the Implicit Flow relies on redirecting iframes/popups/windows back to your application with the response in the hash of the url.
Note, that if you use loginPopup/acquireTokenPopup/acquireTokenSilent, the end user will not really "see" this redirect page, as it is visited only briefly in either a hidden iframe (for acquireTokenSilent) or popup window. As soon as MSAL.js sees that the iframe/popup has been redirected back to your application, the response is parsed and the popup/iframe is closed.
We are working on a new version of the library that will switch to the Auth Code Flow w/ PKCE, which will not use hidden iframes, however, it will still use popups/redirects.
Can you further explain why you don't want your users to visit the redirect page?

Can't login to Skype SDK with non-existing domain name

I have problem about SigninManager. When I login in with tan.tastan#abcd.com, abdc.com is a reachable domain. But if I write a wrong domain, for example tan.tastan#abcd***E***.com, I am not getting a response and my application is waiting. Nothing happens and there is no return error code.
Here is my sample code, settings includes username, password, and domain information.
function doLogin(settings) {
return new Promise((resolve, reject) => {
window.skypeWebSDKApi.signInManager.signIn(settings).then((response) => {
resolve(response);
}, (error) => {
reject(error);
}).catch(reject);
});
}
What is the problem?
It's hard to know exactly what is going on without seeing the contents of settings but I suspect you're issue here is a promise not getting resolves. Try simplifying your call:
function doLogin(settings) {
var app = new api.application;
app.signInManager.signIn(settings).then(function () {
console.log('success');
}, function (error) {
console.log(error);
});
}
I've been using the SDK for quite a while now and this is my experience:
When trying to log in using a non existing domain, the Web SDK never returns an error. I've tried different SDK versions and both General Availability and Public Preview API keys.
I ended up starting my own signin timer when trying to sign in.
When no response is received within 20 seconds, I send a signOut request (which cancels the sign in) and show a message to the user (Please make sure you've entered the right username etc..).
It's really lame to have a workaround like this but unfortunately I haven't found a better way yet to deal with this issue, also assuming Microsoft is not going to fix this anymore...

Refresh Office365 Authorization Token from Javascript

I currently have an issue with one of my apps.
The issue is that a user can keep the page open for a prolonged period of time before entering any data, so occasionally, when they enter data and hit the submit button, they are redirected to o365 for authentication and therefore lose the entered data.
I have all the standard authentication working for the app. However, i believe in order to do this, i would need to get a refreshed token in javascript when the submit button is clicked, and send this token to an api method in order to give access.
Is this possible and does anybody know how to go about it?
It is an MVC ASP.NET application using Owin O365 security with Microsoft Azure AD.
I am not sure what information or code snippets would be relevant here so if there is anything i can provide, please ask.
I have found multiple examples of getting tokens etc with angular, however, this is not an SPA and does not use angular.
Many Thanks in advance.
UPDATE
I have attempted to retrieve a token using ADAL JS using the following code but it doesnt seem to recognise the AuthorizationContext(config) call:
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.min.js"></script>
$('#btnSubmit').on('click', function (e) {
e.preventDefault();
CheckUserAuthorised();
});
function CheckUserAuthorised() {
window.config = {
instance: 'https://login.microsoftonline.com/',
tenant: '##################',
clientId: '###################',
postLogoutRedirectUri: window.location.origin,
cacheLocation: 'localStorage'
};
var authContext = new AuthorizationContext(config); //THIS LINE FAILS
var user = authContext.getCachedUser();
if (!user) {
alert("User Not Authorised");
authContext.login();
}
else {
alert('User Authorized');
}
}
This gives the following error in console:
'AuthorizationContext' is undefined
UPDATE
I have no got passed the undefined error. This was because i was calling AuthorizationContext rather than AuthenticationContext. Schoolboy error. However now, whenever i check the user property of the context, it is always null. And i do not know a way around this as the context is initialised on page load.
There is a lack of a step in your code, here is a simple code sample, hope it will help you:
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.10/js/adal.min.js"></script>
<body>
login
access token
get user
</body>
<script type="text/javascript">
var configOptions = {
tenant: "<tenant_id>", // Optional by default, it sends common
clientId: "<client_id>",
postLogoutRedirectUri: window.location.origin,
}
window.authContext = new AuthenticationContext(configOptions);
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
function getToken(){
authContext.acquireToken("https://graph.microsoft.com",function(error, token){
console.log(error);
console.log(token);
})
}
function login(){
authContext.login();
}
function getUser(){
var user = authContext.getCachedUser();
console.log(user);
}
</script>
The code sample is from the answer of No 'Access-Control-Allow-Origin' header with Microsoft Online Auth. The issues are different but they are in the same scenario.
any further concern, please feel free to let me know.
I found a similar example on the web and your problem seems to be related to the object you are instantiating.
Instead of
new AuthorizationContext(window.config);
try
new AuthenticationContext(window.config);
The code ran just fine showing that the user was not authenticated.

AWS Cognito - Developer Authenticated Identities in JavaScript(Browser)

I have trouble getting credentials in a browser script.
The authentication server returns cognito_identityId and cognito_token.
Then I set a Cookie:
$.cookie('cognito_identityId')
$.cookie('cognito_token')
I tried to get credentials in 4 ways on the browser, and all Failed:
CognitoIdentityCredentials
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:xxxxxxxxxxxx'
IdentityId: $.cookie('cognito_identityId'),
Logins: {
'myauth': $.cookie('cognito_token')
}
});
// => Error: Missing required key 'IdentityId' in params
assumeRoleWithWebIdentity
var params = {
RoleArn: 'arn:aws:iam::xxxxxxxxxxxx:role/Cognito_xxxxxxxAuth_Role',
RoleSessionName: 'xxxxxxxxxxx',
WebIdentityToken: $.cookie('cognito_token'),
DurationSeconds: 900,
ProviderId: 'myauth'
};
var sts = new AWS.STS({apiVersion: '2011-06-15'});
sts.assumeRoleWithWebIdentity(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
// => AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity
PolicyDocument
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1:xxxxxxxxxxxxx"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}
GetCredentialsForIdentity
var params = {
IdentityId: $.cookie('cognito_identityId'),
Logins: {
"myauth": $.cookie('oauth.io_token')
}
};
var cognitoidentity = new AWS.CognitoIdentity({apiVersion: '2014-06-30'});
cognitoidentity.getCredentialsForIdentity(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log(data); // successful response
}
});
// => InvalidParameterException: Please provide a valid public provider
WebIdentityCredentials
AWS.config.credentials = new AWS.WebIdentityCredentials({
RoleArn: 'arn:aws:iam::xxxxxxxx:role/Cognito_xxxxxxxxxxAuth_Role',
WebIdentityToken: $.cookie('cognito_token')
});
// => Error: There were 2 validation errors:
// * MissingRequiredParameter: Missing required key 'IdentityPoolId' in params
// * MissingRequiredParameter: Missing required key 'IdentityId' in params
Questions:
What am I doing wrong?
What is the correct way to use this?
Thank you.
Thank you for your kindness.
I tyied your advice, but did not change.
Error messages.
POST https://cognito-identity.us-east-1.amazonaws.com/ 400 (Bad Request)
POST https://cognito-identity.us-east-1.amazonaws.com/ 400 (Bad Request)
Error: Missing required key 'IdentityId' in params
at fail (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2163:37)
at validateStructure (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2084:14)
at validateMember (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2110:21)
at validate (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:2059:10)
at Request.VALIDATE_PARAMETERS (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:800:32)
at Request.callListeners (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:3913:20)
at callNextListener (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:3903:12)
at chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:787:9
at finish (chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:126:7)
at chrome-extension://hmjdjbikinkmjbilihjibcihbkbjdgjf/bower_components/aws-sdk-js/dist/aws-sdk.js:142:9
There are source code below link.
https://github.com/bisque33/my-custom-dictionary
and server side is a AWS Lambda Function.
var aws = require('aws-sdk');
aws.config.region = 'us-east-1';
var cognitoidentity = new aws.CognitoIdentity();
var identityPoolId = 'us-east-1:0dccff0d-5fd7-4d14-b38f-d27204feaecc';
console.log('Loading function');
exports.handler = function(event, context) {
console.log('token: %s', event.token);
var params = {
IdentityPoolId: identityPoolId,
Logins: {
'oauth.io': event.token
}
};
cognitoidentity.getOpenIdTokenForDeveloperIdentity(params,function(err,data){
if(err){
console.log(err);
context.fail('Something went wrong');
}else{
context.succeed(data);
}
});
};
This program is Google-Chrome-Extension.
AWS Lambda Function returns token by getOpenIdTokenForDeveloperIdentity.
app/scripts/popup.js calls Lambda Function and set cookies.
app/scripts/background.js calls AWS.config.credentials.get, and returns error.
Am I using it wrong?
Update for Additional Information
Thank you for the additional information.
Error appears on 104 line on background.js
AWS.config.credentials.get(function(){
and 115 line on background.js
dataset.synchronize(
And, My explaination was not enough. Facebook authentication needs the domain(ex. http :// example.com). However, Google-Chrome-Ext does not have domain. It has a domain 'chrome-extension://xxxxxxxxxxxxxxxxxxxx'. Then, I use https://oauth.io. It proxies any authentication and accepts chrome-extension domain.
Popup.js does Facebook authentication through oauth.io sdk. It gets a facebook token, and gives to getOpenIdTokenForDeveloperIdentity. I think facebook token.substr(0,14) is unique. But, If it is wrong, I use another unique identifier(ex. email-address.)
Sorry, I was wrong. AWS.config.credentials.get gives an Error:
Error: Invalid login token.
And, dataset.synchronize shows this Error:
Error: Missing required key 'IdentityId' in params
The first approach you have, using CognitoIdentityCredentials, is most likely the best approach for you to take. I can't spot exactly what's causing the error for you but lets try a couple things:
When using Developer Authenticated Identities, you do need to specify the IdentityId when initializing CognitoIdentityCredentials. You need to get the IdentityId value from the call to GetOpenIdTokenForDeveloperIdentity. However, you shouldn't need to preserve the IdentityId value in a cookie as CognitoIdentityCredentials will cache the id by default in the browser's local storage.
As for your Logins map: It looks like you're trying to use Developer Authenticated Identities. With the JavaScript SDK, use the key 'cognito-identity.amazonaws.com' and make sure the value is the token returned from your backend's call to getOpenIdTokenForDeveloperIdentity.
If you continue to have problem using the CognitoIdentityCredentials approach, please reply here with some more info such as the exact method/code you're calling when you receive the error message, and the traced output (i.e. with console.log('%o',..)) of the params input just before your call to the CognitoIdentityCredentials constructor.
Update Based on Additional Information Provided
I still need to know exactly which line of code you receive the error on, but based on the information provided I think I can still help...
Based on what I see in background.js, it looks like you're trying to initialize CognitoIdentityCredentials using a Developer Authenticated Identities provider. This is where I'm guessing that you're receiving the error.
However, in Popup.js, it looks like you're trying to authenticate the user with Facebook. If you're authenticating your users with Facebook, you should just pass the facebook access token into your Logins map when using Cognito. Just use graph.facebook.com as the key in the Logins map and the access token from Facebook. More detail on how to do this is in the Facebook Integration topic of the Amazon Cognito developer guide.
Facebook vs Developer Authenticated Identities
We can get Developer Authenticated Identities to work for you, but in this case, it doesn't look like the right solution for you since you're not actually doing any additional authentication on the identity in your Lambda function and the unique user identifier that you're passing into the getOpenIdTokenForDeveloperIdentity operation appears to be the facebook token, which is not good by the way since the token itself will change between user sessions even for the same user. Usually a good unique identifier is an email address or a user id used by an internal system.
Facebook Login & Redirects
Since you're ultimately trying to use Facebook for login and Amazon Cognito has built-in integration for Facebook, the best thing for you to do is get an access token from Facebook and pass in the Facebook token to Cognito's login map directly. I'm not sure if this will work with Auth.io or not (I'm just not familiar with it), but as long as Auth.io gives your JavaScript code a bonefide facebook token and you add the same Facebook App ID to both Auth.io and Amazon Cognito's Console, it should work. However, you mentioned you want to use Auth.io to avoid Facebook doing a redirect to a landing page. I could be mistaken, but I'm pretty sure if you're using Facebook's JavaScript SDK you won't need a redirect page. You should only need the redirect page if you're doing Facebook's Manually Build a Login Flow.

What oauthRedirectURL in openFB should look like, using a cordova app?

I came across this openFB plugin to make facebook requests without the sdk, that can be used in cordova,
I got it to log in the user in facebook, the thing is as oauthRedirectURL I end up in a white page, that says Success and I'm not sure how to get the user back to the app,
if (runningInCordova) {
oauthRedirectURL = "https://www.facebook.com/connect/login_success.html";
}
Question is,
What url can i use to point my app ?
User ends up in this screen after the login
-edit-
I found solutions like http://localhost.com/oauthcallback.html but I don't have a apache2 in the cordova environament..
-2nd edit-
This is my current code,
openFB.init({appId: 'xxxxxxxxyyyyyyyy'});
openFB.login( function(response) {
if(response.status === 'connected') {
alert('Facebook login succeeded, got access token: ' + response.authResponse.token);
} else {
alert('Facebook login failed: ' + response.error);
}
}, {scope: 'email'});
This the line of the lib that fills this value
if (runningInCordova) {
oauthRedirectURL = "https://www.facebook.com/connect/login_success.html";
}
I haven't used openFB before but I'm pretty sure it's based on the following docs:
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.3
If you go to the section "logging people in" you'll see the following message:
redirect_uri. The URL that you want to redirect the person logging in
back to. This URL will capture the response from the Login Dialog. If
you are using this in a webview within a desktop app, this must be set
to https://www.facebook.com/connect/login_success.html.
When a FB user grants permissions to your App, it will be redirected to the url https://www.facebook.com/connect/login_success.html?access_token=new_token&...
What you have to do now is monitor this url and get the access token provided, which you should store with the fb user id in order to perform any API call.
Googling how to do this with openFB I found a thread at the openFB github repo that should help: https://github.com/ccoenraets/OpenFB/issues/20#issuecomment-49249483 (is not totally related but it provides some code you can use)
This should be the code that will allow you to monitor the URL (extracted from the code provided on the thread):
if (runningInCordova) {
loginWindow.addEventListener('loadstart', function (event) {
var url = event.url;
if (url.indexOf("access_token=") > 0) {
// Get the token
}
});
}
Once you have obtained the access token and stored in your database, you should redirect to any other place of your App.
I hope it helps.
Javier.

Categories

Resources