Cordova app using angular & ADAL - angularjs

I'm building my first mobile app using Cordova. The back-end services live on Azure so I'm trying to get authentication working by using the ADAL plugin for Cordova.
First of all I found out that the library does not do intercepts as the ADAL library for Angular does. I'm using Angular within my Cordova app, paired with material design directives for the look-and-feel. Would have been nice to have interception, but as I understood it's just not there at the moment (should find out how hard it is to implement).
So instead I now wrote a service which will take care of sending REST api requests to Azure, including the correct authentication token. It's based on the sample found here.
This is what I came up with:
var request = function(url)
{
createContext()
.then(function () {
getAuthToken().then(
function(token) {
sendRequest(token, url);
})
},
function (err) {
$log.error("Failed to create a context.");
});
};
First it will create the authentication context:
function createContext () {
return $q(function (resolve, reject) {
var authenticationContext = Microsoft.ADAL.AuthenticationContext;
authenticationContext.createAsync(authority)
.then(function (context) {
authContext = context;
$log.log("Created authentication context for authority URL: " + context.authority);
resolve();
}, function (err) {
$log.error("Failed to create authentication context: " + pre(err))
reject();
});
});
};
The using the context it should get the authentication token:
function getAuthToken()
{
if (authContext == null) {
$log.error('Authentication context isn\'t created yet. Create context first');
return;
}
return $q(function (resolve, reject) {
authContext.acquireTokenAsync(resourceUrl, appId, redirectUrl)
.then(function (authResult) {
resolve(authResult.accessToken);
}, function (err) {
$log.error("Failed to acquire token: " + pre(err));
reject();
});
});
}
And afterwards it should send the request but I'll leave that part out since it never gets there anyway. I feel the need to re-emphasize that I'm a complete n00b at this stuff, so please be easy on me and especially on the code. There's probably a lot of room for improvement, I get that.
When I actually run this, it pops up a window where I need to login using my Microsoft account, cool. I even got two factor authentication first time I tried this, very nice! So I log in and I get returned to the code. But now the authresult variable has a status of "Failed" and there's no access token in the result. Unfortunately there's also no indication of what went wrong. So first part of the question is; what could have gone wrong here?
Now we get to the second part of the question; how do you properly debug these kinds of things? On my desktop I'd run Fiddler to check out the communication, but I don't know how to do that for Android. I'm debugging on my device btw, cause for some reason all of the emulators available to me are extremely slow (VS and Google) even though my hardware specs should support them just fine.
Thanks for any pointers!
Update 03-02-2016
Fiddling around with the code a bit, I decided to pack things in a login function which gives a somewhat shorter sample:
var createContext = function () {
if (authContext == null) {
authContext = new Microsoft.ADAL.AuthenticationContext(authority);
}
};
var getAuthToken = function () {
if (authContext == null) {
$log.error('Authentication context isn\'t created yet. Create context first');
return;
}
return $q(function (resolve, reject) {
authContext.acquireTokenAsync(endpointUrl, appId, redirectUrl)
.then(function (authResult) {
resolve(authResult.accessToken);
}, function (err) {
$log.error("Failed to acquire token: " + pre(err));
reject();
});
});
}
var login = function () {
createContext();
getAuthToken();
}
This code runs on the following input vars:
var authority = 'https://login.windows.net/[tenantid]';
var resourceUrl = 'https://graph.windows.net/';
var appId = '1ef41b17-0943-4359-bc12-014f4fd2d841';
var redirectUrl = 'http://MyApp';
I now used chrome://inspect to see what is going over the wire. And to my big surprise, I see a valid SAML token returned from Azure. It has got my name in it and everything, which I'd recon they wouldn't send after a failed authentication. So it seems that even though the response is ok, the ADAL library doesn't give me a proper response (Status = Failed). Again no clue on how to proceed :S

I just solved it. And like one would expect, the remedy is as simple as they get. Configuring the application in Azure AD, I chose the "web application" type application, since this is a web application with Angular and all. Now I guess since Cordova translates things to native code, that's not the correct option to chose. As soon as I created a new application as "native application" instead and used the client ID of that one, everything started working.... Sincerely hope this will help someone else in the future...!

I had a very similar issue where I was trying to access a web api from a Cordova app. I was using the App ID Uri for the web api I wanted to access as the resouceURL when calling acquireTokenAsync. When I changed this to the client Id of the Web Api instead it worked.

Related

Google SignIn: Redirect Uri in GSI client flow

I’m trying to use the new Google Identity Services to sign in an user to get access to manage his calendars.
My current auth function looks like this in JS:
const auth = async () => {
return new Promise((resolve) => {
const GTokenClient = google.accounts.oauth2.initTokenClient({
client_id: GOOGLE_CLIENT_ID,
scope: GOOGLE_CALENDAR_SCOPE,
prompt: '',
callback: resolve
});
if (gapi.client.getToken() === null) {
GTokenClient.requestAccessToken({ prompt: 'consent' });
} else {
GTokenClient.requestAccessToken({
prompt: ''
});
}
});
};
In desktop web browsers it works fine and the promise resolves but in smartphones (currently trying with an iPhone12) the browser opens a new tab and it stays there loading after giving permissions.
I’m aware that you can set a redirect with the code flow, but it is possible to do the same with the client flow?
I don’t know what to do honestly because there are no examples to implement this behavior using the client flow in the Google documentation.
I only want to be able to redirect the user to the initial screen, if I close the tab that google creates for signing in the calendar is loaded and everything seems fine, it is just a matter of redirection.

How can I access Google Fit data without logging in

I have a web page I am working on, I am wanting to use gapi to access my steps for the day. I do not want to log in every time, I want the page to log in automatically in the background, oAuth2 is required I believe but I can not get any further.
Based on code I've found online and using React with Hooks, I added ts ignore because it could not resolve gapi.
The issue I am having is that I get Login Required error
I've tried using Axios and doing it by API_KEY only. My knowledge on API's is growing but I thought just to access the data an API key would be enough providing I had registered the key in the API tools.
React.useEffect(() => {
const start = () => {
// 2. Initialize the JavaScript client library.
// #ts-ignore
gapi.client.init({
'apiKey': '<API_KEY>',
// clientId and scope are optional if auth is not required.
'clientId': '<CLIENT_ID>.apps.googleusercontent.com',
}).then(function() {
// 3. Initialize and make the API request.
// #ts-ignore
return gapi.client.request({
'path': 'https://www.googleapis.com/fitness/v1/users/<MY_GMAIL_ADDRESS>/dataset:aggregate',
'method': 'POST',
'body': {
"aggregateBy": [{
"dataTypeName": "com.google.step_count.delta",
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
}],
"bucketByTime": { "durationMillis": 86400000 },
"startTimeMillis": 1567983600000,
"endTimeMillis": 1568057160150
},
})
}).then(function(response) {
console.log(response.result);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// #ts-ignore
gapi.load('client', start);
}, []);
I just want to be able to return my steps in a JSON object preferably with Axios, if not using the gapi JS library using my React Application. Nothing fancy.
I appreciate this may not be possible but the docs and other questions on stack overflow just are not working for me and no one can answer me.

Reactjs AWS Cognito - How to Handle newPasswordRequired

I'm using AWS Cognito Javascript SDK in a react application. I have a user that was created in the AWS Console by an admin. The user recieves an email with their username and temporary password. Now based on my understanding, I have to go through the newPasswordRequired flow, but I have been struggling with this for several hours now trying multiple different approaches and none are getting me anywhere. When I check the AWS Console, the user in the user pool is set to FORCE_CHANGE_PASSWORD.
Here is my code in its current state. Please if someone can help me solve the process as I am fairly new to using Cognito authentication.
function setNewPassword(data) {
console.log("data \n", data)
var authenticationData = {
Username: data.username,
Password: data.temp_password
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(
authenticationData
);
var userPool = new AmazonCognitoIdentity.CognitoUserPool(config.cognito);
var userData = {
Username: data.username,
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
return new Promise(function(resolve, reject) {
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
resolve(resolve);
},
onFailure: function(err) {
reject(err);
},
newPasswordRequired: function(userAttributes, requiredAttributes) {
this.cognitoUser.completeNewPasswordChallenge(newPassword, attributesData, this)
}
});
});
}
In the browser console, I am getting the following error:
{code: "UnknownError", message: "Unkown error"}
Have you looked at using the AWS Amplify JS library for help here? There's a suite of React components already built to help with these sorts of things. Here's a link to the relevant documentation. The code is open sourced on GitHub and you might be able to just use the RequireNewPassword component, or at least find inspiration from it.

how to replace url with new url without page refresh in angular js

I want to refresh a URL upon successful login without a page refresh.
I am using the following code
$scope.Login = function (teacher, chkRememberMe) {
alert(chkRememberMe);
$http.post("/Teacher/Login", { teacher: teacher, chkRememberMe: chkRememberMe })
.success(function (result) {
if (result == "True") {
toastr.success("You are Login successfully!!!");
$timeout(function () { }, 5000);
$timeout(function () { }, 5000);
$location.path('Teacher/Index');
}
else {
toastr.error("The email and password you entered don't match.")
window.location = "/Teacher/Login";
}
})
.error(function (error) {
error.message
alert(error);
toastr.error("The email and password you entered don't match11")
})
}
The URL is not updating successfully.
You can use history.replaceState(data,title,url) to accomplish this. There's a bit of documentation at MDN. The history.js library may be of interest as well.
If you're trying to handle single-page authentication with angular, it can be a hassle. You may want to take a look at AngularJS: Basic example to use authentication in Single Page Application for more on this.
An aside: you may also want to consider taking steps to ensure that password managers function as you'd expect. There are a few stackoverflow questions which address these concerns (e.g., How to make Chrome remember password for an AJAX form?).

Angular-permission define permissions retrieved via API

I am trying to use angular-permission to implement permission-based authentication but I don't know where to define those permissions which are retrieved from my back-end via API which requires token-based access.
First, let me give a bit background about how my app looks like. On my back-end, my system portal, I define permissions to allow different APIs to be called. Permissions won't change all the time. Only when I add new features(APIs), new permissions will be added. For example.
permission1: api1,api2,api3
permission2:api4,api5,api6
permission3:api7,api8,api9
On the front-end, customers login the front-end web portal and create customized roles themselves which group some permissions together, for example:
admin: permission1,permission2,permission3
auditor:permission 3
The angular-permission doc says (https://github.com/Narzerus/angular-permission/blob/development/docs/1-manging-permissions.md#multiple-permissions) I can use PermissionStore.defineManyPermissions to define permissions which are retrieved from API after user login. That's all clear.
So I have two modules. One is the Authentication module which handles user login. The other one is the Permission module which handles the permission validation. On the Permission module .run() phase, I define the permissions like this:
var getPermissions = function () {
var deferred = $q.defer();
system.permissions.get(
function () {
return deferred.resolve(system.permissions._permissions);
},
function (error) {
console.log("error if can't load permissions");
console.log(error);
}
);
return deferred.promise;
};
var loadPermissions = function () {
var promise = getPermissions();
promise.then(function (permissions) {
var arrayPermissions = formatPermissionArray(permissions);
//var arrayPermissions=['viewSeed','viewAuthentication'];
PermissionStore.defineManyPermissions(arrayPermissions, checkPermission);
console.log("from permission run service");
console.log(arrayPermissions);
}, function (reason) {
console.log('Failed: ' + reason);
}, function (update) {
console.log('Got notification: ' + update);
});
};
loadPermissions();
var formatPermissionArray = function (sourceData) {
var formatedPermissionArray = [];
for (var i = 0; i < sourceData.length; i++) {
formatedPermissionArray.push(sourceData[i].permissionId);
};
return formatedPermissionArray;
};
But during the bootstrap of the app, this module already loaded and the arrayPermissions will be empty since user hasn't logged in yet.
I tried to use oclazyload to load the Permission module from the login controller of the Authentication module, that actually works but if user refresh/reload their page, the Permission module won't be loaded anymore.
I am new to web development and also new to AngularJs. Just a few months experience. I don't know if I am doing it in a complete wrong way.
My questions are:
The API for retrieving a permission list should require authentication? Since I will need to put those authentication on the UI-router routes. Anyone can see it anyway. If I should not protect that API, then my problem is solved.
If I should keep my api protected, how should I address the issues I described above and that is where to define the permissions for angular-permission and how to use API to retrieve the permissions.
I hope I have managed to describe my issues clearly. Any help or guidance are greatly appreciated.
Regards,
Lola
I'm using angular-permission with angular-satellizer. PermRoleStore or PermPermissionStore needs to be in run block. You can add data to JSON WEB TOKEN add use it at the run block like I did.
$auth.getPayload()This function returns payload from JWT in localStorage. And in that payload it has data with role key which I saved in backend. I hope this helps your issue.
.run(function (PermRoleStore, $auth, Yollar) {
PermRoleStore
.defineRole('ADMIN', function () {
if($auth.getPayload()) {
if ($auth.getPayload().data.role === 'ADMIN') {
return true;
}
else {
return false;
}
}
else {
return false;
}
});
PermRoleStore
.defineRole('MODERATOR', function () {
if($auth.getPayload()) {
if ($auth.getPayload().data.role === 'MODERATOR') {
return true;
}
else {
return false;
}
}
else {
return false;
}
});
})

Resources