How to download a file using Angular with Bearer token - angularjs

we have secured our application using Azure AD + OpenID connect. The client application is developed in Angular and we are using 'angular-adal' library for integrating with azure ad. So whenever client makes api call to the server, it automatically includes bearer token in the request header. ( on the server we have 'passport-azure-ad' node library which validates the token)
We have download file functionality which is currently implemented as blow
Controller
$scope.getURL = function (reportId) {
return '/api/reports/download/' + reportId;
};
HTML
<form method="get" action="{{getURL(row.id)}}">
<button class="btn btn-link" type="submit">Download Results</button>
</form>
However it does not include the bearer token in the request when i click on download button, so server returns not authorized error. How do i included token in the request? whats the best way?
UPDATE1
As per the recommendation by Angular ADAL library, we can secure the route by setting 'requiredADLogin' property to true in $routeProvider. Like below
$routeProvider.
when("/todoList", {
controller: "todoListController",
templateUrl: "/App/Views/todoList.html",
requireADLogin: true
});
I am using $stateProvider. I have set the states for all other html page routes with 'requiredADLogin', and that works fine. How do i set $stateProvider for API route. My Download url is
'/api/reports/download/'+reportID

I'm not familiar with Azure AD but I guess you need to set the Authentication token somehow like this in the header:
var req = {
method: 'GET',
url: '/api/reports/download/' + reportId,
headers: {
'Authorization': 'Bearer ' + token
}
}
$http(req).then(function(){...}, function(){...});

Related

Adal angularjs - Azure active directory : hide id_token from URL/querystring

I'm using adal-angular js library-adal-angular for authentication which generates token. When login is made and it redirects, the token is appended to querystring like this https://localhost:8800/Index.html#id_token=eyJ0eXAiOiJKV1QiLCJhb...
Why this is happening, what should I do to avoid token on URL?
I've checked this and tried the solution but it's not working for me. Anything I'm doing is wrong? Can anyone please help me?
Here is the code I'm using,
app.js
var app = angular.module('app', [
'ngRoute',
'AdalAngular',
]);
app.config([
"$routeProvider",
"$locationProvider",
"adalAuthenticationServiceProvider"
function (
$routeProvider,
$locationProvider,
adalProvider
) {
$locationProvider.hashPrefix("");
adalProvider.init(configuration_object, $httpProvider);
$routeProvider
.when("/abcd", {
templateUrl: urlBase + "abcd.html" + version,
controller: "abcdCtrl",
requireADLogin: true,
})
.otherwise({
redirectTo: "/login",
});
}
loginCtrl.js
angular.module("app").controller("loginCtrl", [
"adalAuthenticationService",
function (
adalService
) {
adalService.login();
}
}
The token is returned in the query string because adal-angular uses the Implicit Grant, where the tokens are returned from the authorization endpoint directly instead of the app acquiring them from the token endpoint.
To hide them from the URLs, you will need to use the Authorization Code Grant with PKCE.
This flow is supported only by the newer versions of MSAL.js, there is an Angular wrapper here: https://www.npmjs.com/package/#azure/msal-angular.
You will also need to change your reply URLs to Single Page Application platform so that this newer flow is supported.
If you want to know how this flow works, the documentation has some details: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow.
Essentially, only an authorization code is passed in the URL, the tokens are acquired from the token endpoint with an HTTP request from your front-end.
Do note in both of these flows the tokens are still visible to the users of your app, since the code runs on their machines :)

Authenticate and Authorise in Both MVC and Http AuthorizeAttribute

I get follow scenario which is working now:
MVC controller using System.Web.Mvc.AuthorizeAttribute to authenticate user is authenticated or not, it will be using cookie.
API controller using System.Web.Http.AuthorizeAttribute to authorise with bearer token.
I do also have angular http interceptor that verify and get bearer token for API purpose that can use among all angular $http request. But I am confusing how to achieve both after user has login?
This is current workflow
User click login, angular verify and store bears token in local storage.
After complete, manually trigger MVC controller so that it will get cookie for MVC authenticate.
This seem to me really double job, or I should focusing on using one AuthorizeAttribute?
You need you use Authorize key to give permission to those functions where authorization is needed. And those functions can only be accessed when authorization token is generated and passed with http request.
module.service('tokenservice', function ($http) {
this.get = function () {
var accesstoken = sessionStorage.getItem('accessToken');
var logged_in = localStorage.getItem('logged_in').toString().trim() === 'false' ? false : true;
var authHeaders = {};
if (accesstoken && logged_in) {
authHeaders.Authorization = 'Bearer ' + accesstoken;
}
return authHeaders;
};
});
module.controller('yourControllerName', function ( $http, tokenservice) {
$http({
method: "POST",
url: '/Controller/MyFucntion',
headers: tokenservice.get(),
});
});
This will help you to get generated token in user login. After that You need to work with your controller
[Authorize]
public JsonResult MyFucntion()
{
//Your logic and calculation
//return
}
Hope that will help

Angular JS POST Submission Failure

I'm new to angular JS. I've followed an online tutorial and created a simple login form on the frontend, and linked it to the backend. Or at least I've tried. The backend is a nodejs/express server, which has a route for handling the login attempts from the frontend. It will be checking to see if the username and password used on the form are from an existing user account, or not.
The problem is that for some reason, the http POST call from the angular controller, always results in a ERR_CONNECTION_TIMED_OUT response in the browser console.
The thing is, though if I interface with the api endpoint using curl, it works just fine and the server does exactly what it's supposed to do. Just for some reason the angular frontend form cannot connect to the backend. Here's the angular controller code:
app.controller('loginCtrl, function($scope, $location, $http){
$scope.login = function(){
var parameter = JSON.stringify({ username: $scope.username, password: $scope.password });
$http({
url: 'https://localhost:8443/api/login'
method: 'POST',
data: parameter
}).then(function(response){
console.log('success: ' + JSON.stringify(response));
},
function(response){
console.log('failed: ' + JSON.stringify(response));
});
}
});
The nodejs backend server is serving content over HTTPS. This controller function (login) is being hit, and the POST is being made, but it simply times out. And again, manually interfacing with these api endpoints works as expected when using curl or wget.
Any insight into the issue or what I'm doing wrong?

Angular.js SPA security with ASP.NET MVC and WebApi

I'm building a SPA using Angular.js and ASP.NET and I would like to know what is the best way to secure it.
Here is what I need :
I would like to use MVC framework to hide my application only to logged users. So the first thing that users will do before launching the SPA will be to log into the website using a simple login form.
When the Angular app will be launched, it will communicate with my ApiController using REST requests.
I also want my user to be logged out automatically after 20 minutes of inactivity.
I know that REST is supposed to be stateless... but I can't figure how to implement all I need without sessions...
But on the other side, I want to be able to use my WebAPI with a future mobile application. I will have to use Tokens for the authentication on this application.
What is the best way for me to achieve that kind of authentication?
Thanks for your time!
I developed an entire security layer with the same conditions as yours following those very well explained in this post here.
BTW, the token will expire automatically after 20 minutes because when you create it you will set it's expiration date immediately; every time you're going to make a request, the system will check the token exp date with the current date, refusing your token if the time passed. For example this a tipical oauth server configuration with token and refresh token settings:
internal static OAuthAuthorizationServerOptions GetAuthorizationServerOptions(IComponentContext scope)
{
OAuthAuthorizationServerOptions oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
ApplicationCanDisplayErrors = true,
TokenEndpointPath = new PathString(Constants.PublicAuth.OAUTH_TOKEN_PATH),
AuthorizeEndpointPath = new PathString(Constants.ExternalAuth.AUTH_ENDPOINT),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(Constants.PublicAuth.TOKEN_EXPIRATION_IN_MINUTES),
Provider = scope.Resolve<AuthorizationServerProvider>(),
AccessTokenFormat = new CustomJwtFormat(),
RefreshTokenProvider = scope.Resolve<SimpleRefreshTokenProvider>()
};
return oAuthServerOptions;
}
The refresh token is also very useful, but you have to manage the token replacement by yourself; for example in our application we pass every API call through a single service that, if the server responds 401 (unauthorized), it will try to request a new token using the refresh token and then it will try the same call again. Only after the second failure you'll be redirected to the login page.
For example:
function executeCallWithAuth(method, url, payload, params) {
var defer = $q.defer();
debug.logf('{0}: {1}', method.toUpperCase(), url);
$http({ method: method, url: url, data: payload, headers: createHeaders(), params: params }).then(
function(results) { defer.resolve(results); },
function(error) {
if (error.status !== 401) defer.reject(error);
else {
debug.warn(`Call to: ${method}:${url} result in 401, try token refresh...`);
auth.refreshToken().then(
function() {
debug.warn('Token refresh succesfully, retry api call...');
$http({ method: method, url: url, data: payload, headers: createHeaders() }).then(
function(results) { defer.resolve(results); },
function(errors) { defer.reject(errors); });
},
function(tokenError) {
debug.warn('Token refresh rejected, redirect to login.');
$state.go('login');
defer.reject(tokenError);
});
}
});
return defer.promise;
}
and
function createHeaders() {
var headers = {
};
var authData = storage.get('authorizationData');
if (authData) {
headers.Authorization = 'Bearer ' + authData.token;
}
return headers;
}
Using Angular the best way to secure a route is "do not create a route". Basically, you need to load the user profile, and only after that you will create the routes only to the pages he can navigate to. If you don't create the route for a page you don't need to secure that page: Angular will automatically send the user to a 404.
I would secure your WebAPI calls with OAuth2 (you can even use the built in Identity 2.0 provider that comes baked in with it). Keep your WebAPI stateless, use SSL (consider a filter to force it), and use the [Authorize] tags to secure you services. On the MVC side, this will have to maintain state and you will want to have the login form get an OAuth2 token from your WebAPI layer and pass that down into Angular. Set the expiration on this to 20 minutes. You can also use the cookies authentication model here since it will need to be stateful on the MVC side, but all ajax calls made to the WebAPI layer by Angular will need to pass the OAuth2 token as a bearer token in the Authorization request header.

Node API - How to link Facebook login to Angular front end?

Rewriting this question to be clearer.
I've used passport-facebook to handle login with facebook on my site.
My front end is in Angular so I know now need to understand whats the correct way of calling that api route. I already have several calls using Angular's $http service - however as this login with facebook actually re-routes the facebook page can i still use the usual:
self.loginFacebook = function )() {
var deferred = $q.defer();
var theReq = {
method: 'GET',
url: API + '/login/facebook'
};
$http(theReq)
.then(function(data){
deferred.resolve(data);
})
return deferred.promise;
}
or is it perfectly ok/secure/correct procedure to directly hit that URL in a window location:
self.loginFacebook = function (){
$window.location.href = API + '/login/facebook';
}
Furthermore, from this how do I then send a token back from the API? I can't seem to modify the callback function to do that?
router.get('/login/facebook/callback',
passport.authenticate('facebook', {
successRedirect : 'http://localhost:3000/#/',
failureRedirect : 'http://localhost:3000/#/login'
})
);
Thanks.
I was stacked on the same problem.
First part:
I allow in backend using cors and in frontend i use $httpProvider, like this:
angular.module('core', [
'ui.router',
'user'
]).config(config);
function config($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
};
The second part:
<span class="fa fa-facebook"></span> Login with facebook
This call my auth/facebook route that use passport to redirect to facebook page allowing a user to be authenticated.
If the user grant access, the callback /api/auth/facebook/callback is called and the facebook.strategy save the user with the profile data.
After saving the user, i create a special token with facebook token, id and email. This info is used to validate every time the user access to private states in the front.
My routes are something like this:
router.get('/facebook', passport.authenticate('facebook',
{ session: false, scope : 'email' }));
// handle the callback after facebook has authenticated the user
router.get('/facebook/callback',
passport.authenticate('facebook',
{session: false, failureRedirect: '/error' }),
function(req, res, next) {
var token = jwt.encode(req.user.facebook, config.secret);
res.redirect("/fb/"+token);
});
In frontend i catch the /fb/:token using a state and assign the token to my local storage, then every time the user go to a private section, the token is sent to backend and validate, if the validation pass, then the validate function return the token with the decoded data.
The only bad thing is that i don't know how to redirect to the previous state that was when the user click on login with facebook.
Also, i don't know how you are using the callback, but you need to have domain name to allow the redirect from facebook. I have created a server droplet in digitalocean to test this facebook strategy.
In the strategy you have to put the real domain in the callback function, like this:
callbackURL: "http://yourdomain.com/api/auth/facebook/callback"
In the same object where you put the secretId and clientSecret. Then, in your application in facebook developers you have to allow this domain.
Sorry for my english, i hope this info help you.
Depending on your front-end, you will need some logic that actually makes that call to your node/express API. Your HTML element could look like
<a class='btn' href='login/facebook'>Login</a>
Clicking on this element will make a call to your Express router using the endpoint of /login/facebook. Simple at that.

Resources