How do I implement secure OAuth2 consumption in Javascript?
Solution 1
The Implicit Grant flow (the one you're referring to as User-Agent Flow) is exactly the way to go:
The implicit grant is a simplified authorization code flow optimized for clients implemented in a browser using a scripting language such as JavaScript.
To understand the flow, the documentation from Google for client-side applications is a really good place to start. Note that they recommend you to take an additional token validation step to avoid confused deputy problems.
Here is a short example implementation of the flow using the Soundcloud API and jQuery, taken from this answer:
<script type="text/javascript" charset="utf-8">
$(function () {
var extractToken = function(hash) {
var match = hash.match(/access_token=([\w-]+)/);
return !!match && match[1];
};
var CLIENT_ID = YOUR_CLIENT_ID;
var AUTHORIZATION_ENDPOINT = "https://soundcloud.com/connect";
var RESOURCE_ENDPOINT = "https://api.soundcloud.com/me";
var token = extractToken(document.location.hash);
if (token) {
$('div.authenticated').show();
$('span.token').text(token);
$.ajax({
url: RESOURCE_ENDPOINT
, beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', "OAuth " + token);
xhr.setRequestHeader('Accept', "application/json");
}
, success: function (response) {
var container = $('span.user');
if (response) {
container.text(response.username);
} else {
container.text("An error occurred.");
}
}
});
} else {
$('div.authenticate').show();
var authUrl = AUTHORIZATION_ENDPOINT +
"?response_type=token" +
"&client_id=" + clientId +
"&redirect_uri=" + window.location;
$("a.connect").attr("href", authUrl);
}
});
</script>
<style>
.hidden {
display: none;
}
</style>
<div class="authenticate hidden">
<a class="connect" href="">Connect</a>
</div>
<div class="authenticated hidden">
<p>
You are using token
<span class="token">[no token]</span>.
</p>
<p>
Your SoundCloud username is
<span class="user">[no username]</span>.
</p>
</div>
For sending XMLHttpRequests (what the ajax()
function does in jQuery) using AngularJS, refer to their documentation of the $http
service.
If you want to preserve state, when sending the user to the authorization endpoint, check out the state
parameter.
Solution 2
There's an example of Authorization Code Grant
approach to get a token from OAuth server. I used jQuery ($
) to make some operations.
First, redirect user to authorization page.
var authServerUri = "http://your-aouth2-server.com/authorize",
authParams = {
response_type: "code",
client_id: this.model.get("clientId"),
redirect_uri: this.model.get("redirectUri"),
scope: this.model.get("scope"),
state: this.model.get("state")
};
// Redirect to Authorization page.
var replacementUri = authServerUri + "?" + $.param(authParams);
window.location.replace(replacementUri);
When one gave authorization pass to get token:
var searchQueryString = window.location.search;
if ( searchQueryString.charAt(0) === "?") {
searchQueryString = searchQueryString.substring(1);
}
var searchParameters = $.deparam.fragment(searchQueryString);
if ( "code" in searchParameters) {
// TODO: construct a call like in previous step using $.ajax() to get token.
}
You could implement the Resource Owner Password Credentials Grant
in the same manner using jQuery or pure XMLHttpRequest and don't make any redirects - because on each redirect you'll loose state of your application.
For me, I used HTML5 local storage to persist state of my application for data which were not likely to pose a security threat.
Related videos on Youtube
David Myers
I'm a programming nerd who loves solving problems and finding new ways to do things. I'm primarily a PHP guy, but have a large OO background including Java and C/C++.
Updated on March 19, 2020Comments
-
David Myers about 4 years
I'm in the process of designing an API in PHP that will use OAuth2.0. My end goal is to build a front-end application in javascript (using AngularJS) that accesses this API directly. I know that traditionally there's no way to secure transactions in javascript and so directly accessing an API isn't feasible. The front-end would need to communicate with server code that in turn communicated with the API directly. However, in researching OAuth2 it looks as if the User-Agent Flow is designed to help in this situation.
What I need help with is implementing the OAuth2 User-Agent Flow in javascript (particularly AngularJS if possible as that's what I'm using for my front-end). I haven't been able to find any examples or tutorials that do this. I really have no idea where to start and don't want to read through the entire OAuth2 spec without at least seeing an example of what I'll be looking at doing. So any examples, tutorials, links, etc would be greatly appreciated.
-
David Myers almost 12 yearsThrough related questions on SO, I've found this JS library that appears to simplify and handle what I'm trying to do. github.com/andreassolberg/jso It appears to be rather small and unused. Plus it doesn't completely solve my issue.
-
Abdelghani Roussi almost 9 yearsDid you figured out ?
-
Sourav Das over 7 years@DavidMyers Did you get CORS error while doing this Javascript. How did you solve this
-
-
Jan Gerlinger almost 12 yearsUsing the authorization code flow provides no additional benefits over the implicit grant flow in this case. Additionally, if the API supports different authorization flows for more (hiding the secret) and less (exposing the secret) trusted clients, the server can then differentiate between them and do e.g. additional checks or limit scope. Also the resource owner password credentials flow shouldn't be used in this case, as the resource owner can't have a high degree of trust to JS client code from a third-party website.
-
Jan Gerlinger almost 12 yearsWhich redirect do you mean? The one to the authorization endpoint? That redirect is also part of the implicit grant flow.
-
Björn Lindqvist about 11 yearsTo make this work with latest SoundCloud api, change the regexp to
/access_token=([\w-]+)/
. Their token now includes the dash character. -
Jan Gerlinger about 11 yearsThanks, I edited the answer. There are probably also other characters allowed, but I am not sure if this is documented anywhere.
-
Onshop about 9 yearsAs I understand it the 'implicit' grant type is for third-party apps. There is an expected 'allow authentication' redirect step on the server. You would not want this on a first-party app, i.e. your own client, from a UI perspective (it would be confusing). Therefore the only option is the 'Resource Owner Password Credentials' grant type or develop a 'proxy' service on the server to obtain a bearer token.
-
Jan Gerlinger about 9 years@Ben One of the advantages of implicit and authentication code grant is that the client application (doesn't matter if first or third-party) doesn't have to worry about storing the plain text credentials of the user. It's the typical tradeoff security <-> usability.
-
Onshop about 9 years@Jan. Thanks for the clarification. Does it matter if some generic client credentials are stored in the client in plain text if the user's username and password are needed as well to fully authenticate (and these would not stored or transmitted in plain text)?
-
Jan Gerlinger about 9 years@Ben Well, you risk that some malicious client uses your client credentials to trick users. Depends on your use case if that matters. You can find some additional considerations in the OAuth 2 Threat Model docs.
-
Michael Oryl over 8 yearsThis would be a bad idea. You should never use the authorization grant on a mobile device or in a client-side application if you value your users' and your own security. With this system you would have to store your OAuth2 client secret in the app somewhere where a hacker could easily find it (in source code, for example). If you were to include refresh tokens in the request, then you would potentially be handing over basically permanent keys to the user's data to any hacker with only minor skills.
-
Emile Bergeron over 7 yearsShouldn't it be
xhr.setRequestHeader('Authorization', "Bearer " + token)
? -
Jan Gerlinger over 7 years@EmileBergeron If that is what is presently working with Soundcloud's API, please feel free to suggest an edit to the answer. Soundcloud's API docs anyway seem to favour passing a query parameter with the token now. Not sure, why they would want that?!
-
jle over 7 years@MichaelOryl You don't need to have a client secret for password grant per the spec. Your oauth server should secure the client with whitelisted URLs to prevent illicit use of the client.
-
ecoe about 4 yearsClients SHOULD NOT use the implicit grant and any other response type causing the authorization server to issue an access token in the authorization response. Clients SHOULD instead use the response type "code" (aka authorization code grant type): tools.ietf.org/html/…
-
ecoe about 4 years+1 since auth code grant is recently documented best practice for client code: tools.ietf.org/html/…