I'm developing an single-page with Javascript+AngularJS on the client side and Spring MVC + Spring Security OAuth2 on the server side. Spring MVC acts as a REST controller for any AJAX requests from the page.
For authorization, the script sends an "Authorization: Bearer ..." headers with each AJAX request. This works fine when requesting small amounts of data.
To download XML files (export user data) I download them via AJAX, using the OAuth2 headers and create a Blob to allow saving the file in the browser:
var blob = new Blob([data.data], {'type': "text/xml"});
var a = document.createElement("a");
a.href = window.URL.createObjectURL(blob);
a.download = "downloaded-file-" + new Date().toISOString() + ".xml";
a.click();
This approach works but
Uses RAM and so is unsuitable for large file downloads
Does not show a proper progress/loading bar
So, the question is: is there a better way of downloading files with OAuth2 authorization? Javascript does not allow to specify headers when doing redirects, and OAuth does not allow to specify the authorization token via URL parameters. I'm thinking of either
adding a special Spring MVC controller method to provide an URL which redirects from an URL-encoded token to a header-encoded HTTP request
adding an extra Spring Security filter to allows extracting the token from URL parameters
moving to cookie-based authorization instead of OAuth2
If anyone had similar issues, could you please share your approach to this problem?
I would go with cookies if I were you - it takes all the hassle out of it. I wrote some blogs recently to show how easy it is (e.g. https://spring.io/blog/2015/01/20/the-resource-server-angular-js-and-spring-security-part-iii). People get too hung up on "stateless" applications.
Turns out it's very easy to to in spring-security-oauth2 2.0.7.RELEASE:
Simply pass the access token as the access_token request parameter:
window.open("service/export?access_token=" + access_token);
Now, this will appear with the access token in plaintext in the download history, so for proper security a "logout" option should be properly implemented, or the download will have to be done as a "form post".
Related
Question
If you would use a similar setup as the following examples:
Simple WebAPI
Javascript OIDCClient and usermanager
Would it be possible to protect other clientside files from being accessed? Say for example i have a directory with certain files which you need a certain role to be able to access them.
Would it be possible to protect my SPA from being accessed before logging in?
Or is there a better solution which would have you end up with a protected api, folders/files on a server, SPA and a silent renew mechanism like there is in the OIDCClient?
#dmccaffery helped me out by answering my question, here is his answer for those of you who are interested.
To summarize using the OIDCClient for an SPA is certainly the way to go. Exposing stuff which needs authorization should be done by using an API. Protecting parts of your Angular App can be done using a Route guard.
The way it works is as follows:
The access token is either a JWT or a bearer token (usually) and is
added by the oidc client to every HTTP request in an authorization
header — when a web API receives a reques, the bearer token
authorization middleware will parse this HTTP header and will call the
token introspection endpoint (and potentially the user info endpoint)
to have the token validated and the users claims retrieved… if the
token was manipulated by the client, it will not be valid, and an HTTP
error will be returned (usually a 403). If the token was valid, a
claims identity is created and assigned to the http request context.
The API will now have a thread with an identity already assigned to it
that represents that user.
Also he pointed out 2 pluralsight courses which would probably be useful:
https://www.pluralsight.com/courses/building-securing-restful-api-aspdotnet
https://www.pluralsight.com/courses/oauth2-openid-connect-angular-aspdotnet
I have a web application that gets & stores some data through a Rails API.
When I try to "post" to the Rails API, I get an error ActionController::InvalidAuthenticityToken. I know one option is to simply disable the authenticity token requirement on my Rails controller.
Is it possible to have my web app providing a correct authenticity token when it calls the Rails API? How can I do this?
2 Part answer for you here.
First if you are going to be using Rails as an API I would recommend you use another way of validating that the user making the request is actually the user they say they are such as creating a unique token upon account creation or login that can be returned in the initial response and provided as a HTTP header in subsequent requests. If you are worried about the security of this you could optionally base64 encode the key plus some other value and decode it server side before comparison.
If you still wish to use the CSRF method baked in to Rails you can do so as long as the user is making a request from the webapp using AJAX or whatever. If you have the csrf_meta_tags ERB in the header of your layout file you can get the value and set it in the X-CSRF-Token HTTP header. Using jQuery it may look something like:
$.ajaxPrefilter(function(options, originalOptions, xhr) {
var token = $('meta[name="csrf-token"]').attr('content');
if (token) xhr.setRequestHeader('X-CSRF-Token', token);
});
This would cause it to be added to every ajax request made using jQuery.
We have an API, Oauth2 Provider.
From AngularJS client Side app, how can I implement the flow of authentication to this api to get the access token for future requests?
What I am searching for is a Implicit Grant flow for this.
I'll provide a
{
client-id: "abcdefghijklmnopqrstuvqxyz0123456789",
redirect_url: "http:localhost:8080/provider_cb",
response_type: "token"
}
I have a Rails REST API in the backend and doorkeeper/devise for Oauth2 provision.
I came across angular-oauth, which seems to solve the problem to certain extent.
but,
I do not wish to provide a token verification function (Is this mandatory)
I do not wish to open a new window popup for the login (Wish to do redirections in same window)
Now,
Q1. What I do not understand is how is the whole process started, I can't make any $http request, this returns with a SignIn HTML page. Should I use $location service to redirect to it to login page? Or should I have a link to the whole GET request to /oauth/authorize?...
Q2, How will I capture the redirect after SignIn to extract out the access_token?
Q3. Do know any Service which takes care of Oauth2 authentication or a standard Angular way of doing this?
Lets try an answer to your three questions:
Q1) You should basically understand the general OAuth2 process. It is not an AngularJS-specific implementation you're looking at. If you don't want to work with a popup window for the authorization, you'll have to trick around a bit to have the redirect_url / state working correctly after coming back (if you want to have #state - saving - otherwise just specify your entry - url in the redirect_uri). You should not use $http for the redirection as this is just for XHR and similar calls useful. To get your user to the login page, just do a
$window.location.href = 'http://yourlogin.com/oauthloginpage';
Your app will then redirect to the login page - don't forget your parameters for redirect_url. Also specify other parameters within the request like "scope" / "state" if required.
Q2) The redirect AFTER Login will redirect to the uri you specified within your redirect_url. Then it will come up with something like http://myapp.com/#access_token=foobar&refresh_token=foobar2&state=loremipsem
Just grab the parts from angular with a bit of code like this:
var currentURL = $location.absUrl();
var paramPartOfURL = currentURL.slice(currentURL.indexOf('#') + 1);
Then parse the paramPart into an array with split('&') and iterate through it by looking for the "key" access_token and - voila - there is your access_token ready for being taken into your local storage / service / cookie.
There are other implementations you might use to split URLs into parts - maybe there is a better one, but this here would be the "fast shot".
After parsing, you can redirect the user again to the correct state with a normal $location.path(....) call and eliminate the # parameters of OAuth2.
Q3) If you want to have a way of mapping the source state / route of your AngularJS-app - try misusing the state parameter if your OAuth2-Server implements it. This will survive the request <-> response.
For a non-popup-implementation, I don't know any further module currently.
Another way to play with the OAuth2 stuff is to implement the loginpage on your own and then just interact with a oauth2 provider reacting on rest calls with Basic Auth or other methods. Then you can use $http calls probably.
Our project consists of an MVC area which handles authentication/authorization and rendering of pages, and an API area which also requires authentication/authorization and sends data to the page. We decided to go stateless for the server, so each request must include the authorization header with the user's credentials.
I accomplish this with the API calls with xhr.setRequestHeader('Authorization', 'Bearer ' + authCookie); in jquery's beforeSend, however I am unsure how to do this for the MVC side (each time you click a link or enter a URL, the request should include the Authorization header). Currently I'm doing this inside Application_BeginRequest and setting Request.Headers["Authorization"] = Request.Cookies["auth"];, but I want the Authorization header to be in the initial request, and not just tacked on after the request has been sent.
I believe you won't be able to set Headers; when the browser directs you to a link via an anchor click (unless you catch all anchor clicks using jquery, seems like overkill), nor will headers be sent on Form submits (Get/Post, unless you again catch all forms submissions), and the killer is the fact that server side redirects will also not resend any custom headers.
Instead of answering how to do something in jQuery, I would highly recommend reconsidering your design because based on the above facts, you will most likely run into technical limitations.
I use asp.net mvc controller instead of Web Service in my project.
When I call the controller from my client app,there will be a authentication problem. If I use Web Service ,I can use SOAP Header , but now in asp.net mvc, There is no soap header.
Please help.
I am really know a little about the web security.
Normal way of doing this when you come to http services is to pass it in authorization header in following format (if you are doing request from fiddler)
Authorization: Basic user123:pass123
user123:pass123 string is normally base64 encoded and you have to decode it on server side, check it against user store and authenticate the user. One example can be found here
You have several options.
Use a request header to contain some security token.
Include security tokens in the message that you send in the request body.
If your application uses something like Forms Authentication, you can ask consumers to call a login action, then grab the Forms Auth cookie and include that cookie in subsequent calls.
Since you are not using soap. You may use a simple http way. Which means you start a HttpRequest and handle result via HttpResponse. Thus you have to simulate a authenticate action as signing in from web browser.
You need to get the security token or cookie from the reponse. And put them into your following request. Thus your controller will recognize the request's identity.