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.
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.
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.