How do I implement secure OAuth2 consumption in Javascript?

39,996

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.

Share:
39,996

Related videos on Youtube

David Myers
Author by

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, 2020

Comments

  • David Myers
    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
      David Myers almost 12 years
      Through 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
      Abdelghani Roussi almost 9 years
      Did you figured out ?
    • Sourav Das
      Sourav Das over 7 years
      @DavidMyers Did you get CORS error while doing this Javascript. How did you solve this
  • Jan Gerlinger
    Jan Gerlinger almost 12 years
    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 to JS client code from a third-party website.
  • Jan Gerlinger
    Jan Gerlinger almost 12 years
    Which redirect do you mean? The one to the authorization endpoint? That redirect is also part of the implicit grant flow.
  • Björn Lindqvist
    Björn Lindqvist about 11 years
    To make this work with latest SoundCloud api, change the regexp to /access_token=([\w-]+)/. Their token now includes the dash character.
  • Jan Gerlinger
    Jan Gerlinger about 11 years
    Thanks, I edited the answer. There are probably also other characters allowed, but I am not sure if this is documented anywhere.
  • Onshop
    Onshop about 9 years
    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.
  • Jan Gerlinger
    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
    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
    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
    Michael Oryl over 8 years
    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.
  • Emile Bergeron
    Emile Bergeron over 7 years
    Shouldn't it be xhr.setRequestHeader('Authorization', "Bearer " + token)?
  • Jan Gerlinger
    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
    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
    ecoe about 4 years
    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): tools.ietf.org/html/…
  • ecoe
    ecoe about 4 years
    +1 since auth code grant is recently documented best practice for client code: tools.ietf.org/html/…