Django OAuth Toolkit and Ember Simple Auth

Getting proper authenticated users into your app is always one of the most tedious but essential aspects of getting your project up and running, with this post I hope to take some of the mystery and complexity out of setting up these two projects on a modern EmberJS 4.X stack.

What is OAuth2?

The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf. This specification replaces and obsoletes the OAuth 1.0 protocol described in RFC 5849.

Source: tools.ietf.org

OAuth 2 isn’t a framework like Django or Ember, OAuth2 is a set of standards or rules on how authorization and authentication should be done.

Django-oauth-toolkit

django-oauth-toolkit provides all the endpoints, data and logic needed to add OAuth2 capabilities to a Python Django project.

The quick start to getting it setup in Django:

  • poetry add django-oauth-toolkit
  • modify your settings.py
    • INSTALLED_APPS = ( ... 'oauth2_provider', )
    • REST_FRAMEWORK = {
    • 'DEFAULT_AUTHENTICATION_CLASSES': ( 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', ), 'DEFAULT_PERMISSION_CLASSES': (
    • 'rest_framework.permissions.IsAuthenticated', ) }
  • in urls.py
    • urlpatters = [ ..., url(r'^auth/', include('oauth2_provider.urls', namespace='oauth2_provider')), ]
  • the update your project migratons python manage.py migrate

Now if you try and call on of the api’s in your project you should see a 401 response.

Now to create credentials

if you go to your Django Admin pages (usually /adming) you should now see a section for “Django OAuth Toolkit”. You now want to add a new Application

On the add Application form, you will want to copy your client ID and your Client Secret and save it for later. Other settings

User: Usually your administer user you’ve already setup in django

Client Type: Confidential

Authorization Grant Type: Resource Owner password-based

Press save and we can now get a valid acces token that we can use to authenticate ourselves.

Using a tool like curl you can generate a new token using the following command:

$ curl -X POST -d "grant_type=password&username=<user_name>&password=<password>&scope=read" -u"<client_id>:<client_secret>" http://localhost:8000/auth/token/

and if that worked you should see a response similar to the one below

Now to connect this to our frontend.

Ember-simple-auth

to add ember simple auth to your project just run ember install ember-simple-auth

and now you need to configure it.

from the command line run ember g authenticator django-oauth2 and this will generate the blueprint for your main authentication against django.

open authenticators/django-oauth2.js and add the following:

import OAuth2PasswordGrantAuthenticator from 'ember-simple-auth/authenticators/oauth2-password-grant';
import config from '../config/environment';

/**
 * Authenticator specifically for Django oAuthToolkit
 * 
 **/
export default class DjangoOAuth2Authenticator extends OAuth2PasswordGrantAuthenticator {
  serverTokenEndpoint = `${config.apiHost}/auth/token/`;
  
}

I make a habit of storing my urls in Environment.js but config.apiHost would just point to your local django server.

Now you need to add a login form, i usually create a component called login.js that I can add to any route:

<div class="login-component">
  <form class="login-form" {{on "submit" (action "authenticate")}}>
    <fieldset>
      <div >
        <label for="identification">Username</label>
        <Input id='identification' placeholder="Enter Username" @value={{this.username}} />
      </div>
      <div >
        <label for="password">Password</label>
        <Input type='password' id='password' placeholder="Enter Password" @value={{this.password}} />
      </div>
      <div >
        <button type="submit">Login</button>
        {{#if this.errorMessage}}
          <p>{{this.errorMessage}}</p>
        {{/if}}
      </div>
    </fieldset>
  </form>
</div>
{{yield}}


and in the javascript file:

import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import config from '../config/environment';

export default class LoginComponent extends Component {
@tracked errorMessage;
@tracked username;
@tracked password;
@service session;

@action
async authenticate(e) {
e.preventDefault();
let headers = {
Authorization:
'Basic ' +
btoa(
config.oauth2.providers.djangoOAuth2.clientId +
':' +
config.oauth2.providers.djangoOAuth2.clientSecret
),
};

this.session
.authenticate(
'authenticator:oauth2',
this.username,
this.password,
null,
headers
)
.then(() => {
alert('Success!');
})
.catch((err) => {
this.errorMessage = err;
});
}
}

and here I am using the client ID and Secret from Django stored in environment.js

Now if you run this and log in with your django user you should see the Success alert!

And that’s it, ember-simple-auth tracks your logged in session over multiple browser windows, if you want to restrict access to a route in your application use the session to check the status of the session in the beforeModel hook in your router

// app/routes/authenticated.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class AuthenticatedRoute extends Route {
  @service session;

  beforeModel(transition) {
    this.session.requireAuthentication(transition, 'login');
  }
}

Some usefull links:

Leave a Reply

Your email address will not be published. Required fields are marked *