47

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
  • 799
  • 1
  • 9
  • 18

2 Answers2

50

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.

Community
  • 1
  • 1
Jan Gerlinger
  • 7,361
  • 1
  • 44
  • 52
  • 2
    To make this work with latest SoundCloud api, change the regexp to `/access_token=([\w-]+)/`. Their token now includes the dash character. – Björn Lindqvist Mar 14 '13 at 13:37
  • Thanks, I edited the answer. There are probably also other characters allowed, but I am not sure if this is documented anywhere. – Jan Gerlinger Mar 18 '13 at 11:02
  • As 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. – Onshop Apr 10 '15 at 13:52
  • 1
    @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. – Jan Gerlinger Apr 13 '15 at 10:49
  • @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)? – Onshop Apr 25 '15 at 23:01
  • 2
    @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](https://tools.ietf.org/html/rfc6819#section-4.1.1). – Jan Gerlinger Apr 27 '15 at 14:06
  • 2
    Shouldn't it be `xhr.setRequestHeader('Authorization', "Bearer " + token)`? – Emile Bergeron Aug 08 '16 at 15:14
  • @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](https://developers.soundcloud.com/docs/api/reference#me) anyway seem to favour passing a query parameter with the token now. Not sure, why they would want that?! – Jan Gerlinger Aug 09 '16 at 11:44
  • Clients 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): https://tools.ietf.org/html/draft-ietf-oauth-security-topics-09#section-2.1.2 – ecoe Apr 26 '20 at 02:30
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.

Artem Oboturov
  • 4,344
  • 2
  • 30
  • 48
  • Using 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](https://tools.ietf.org/html/draft-ietf-oauth-v2-30#section-1.3.3) to JS client code from a third-party website. – Jan Gerlinger Jul 17 '12 at 09:29
  • Which redirect do you mean? The one to the authorization endpoint? That redirect is also part of the *implicit grant* flow. – Jan Gerlinger Jul 17 '12 at 13:15
  • This 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. – Michael Oryl Dec 22 '15 at 03:05
  • @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. – jle Nov 15 '16 at 22:12
  • +1 since auth code grant is recently documented best practice for client code: https://tools.ietf.org/html/draft-ietf-oauth-security-topics-09#section-2.1.2 – ecoe Apr 26 '20 at 02:33