Search code examples
authenticationember.jsember-simple-auth

multiple login routes using ember-cli-simple-auth


I am trying to configure a basic ember-cli app using authentication via ember-cli-simple-auth and want to have a dedicated 'guest' login page and a different 'admin' login page (authorizing to different serverTokenEnpoint's).

I have the 'guest' page working, i.e. if a user tries to browse to a protected route (page) then they are redirected to the default /login route and can login Ok.

What I can't figure out is how to have a user that browse's to /admin/xyz route that they then get redirected (using to /admin/login which in turn will authenticate to a different serverTokenEnpoint to the default.

Can anyone point me in the right direction to achieve the above?

Thanks.

an example protected 'guest' route file looks like:

FILE: /app/routes/protected.js

import Ember from 'ember';

import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin);

And the environment configs contain:

FILE: /app/config/environment.js

ENV['simple-auth'] = {
    authorizer: 'simple-auth-authorizer:oauth2-bearer',
    store: 'simple-auth-session-store:local-storage',
    crossOriginWhitelist: ['http://www.domain.com/token',
                         'http://www.domain.com'
   ]
};

I even tried to override the default authenticationRoute in my /app/routes/admin.js file like below but did not work:

import Ember from 'ember';

import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin,{
    authenticationRoute: 'admin.login'
});

So to simplify the process following Marco's suggestion I now have:

Note: AT the moment this does not work.. @marcoow do you have any thoughts where im going wrong?

This is using ember-cli with the follow firebug output:

AuthenticatorBase A (unknown mixin)  ***<- IS this expected????***
CustomAuthenticator B (unknown mixin)
DEBUG: -------------------------------
DEBUG: Ember                       : 1.7.0
DEBUG: Ember Data                  : 1.0.0-beta.9
DEBUG: Handlebars                  : 1.3.0
DEBUG: jQuery                      : 1.11.1
DEBUG: Ember Simple Auth           : 0.6.4
DEBUG: Ember Simple Auth OAuth 2.0 : 0.6.4
DEBUG: -------------------------------

and if I put my manual override code back in see previous answer it will work, but since I want to use the same oauth2 authentication just to a different URL, I like the idea of just being able to override the TokenEndpoint with a custom authenticator.

file: app/initializers/simple-auth-admin.js

import AuthenticatorBase from 'simple-auth-oauth2/authenticators/oauth2';
var CustomAuthenticator = AuthenticatorBase.extend({
    serverTokenEndpoint:            AppchatENV['simple-auth-admin'].serverTokenEndpoint,
    serverTokenRevokationEndpoint:  AppchatENV['simple-auth-admin'].serverRevokationTokenEndpoint,
    refreshAccessTokens:            AppchatENV['simple-auth-admin'].refreshAccessTokens
});
console.log("AuthenticatorBase A ",AuthenticatorBase);
console.log("CustomAuthenticator B ",CustomAuthenticator);

export default {
    name:   'simple-auth-admin',
    before: 'simple-auth',
    initialize: function(container) {
        container.register('simple-auth-authenticator:admin', CustomAuthenticator);
    }
};

But the above shows an error of "AuthenticatorBase A (unknown mixin)"

and then in file: app/controllers/admin/login.js

import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Controller.extend(LoginControllerMixin, {
    authenticator: 'simple-auth-authenticator:admin'
}

and for the configs...

file: config/environment.js

ENV['simple-auth-admin'] = {
    serverTokenEndpoint: "http://www.domain.com/admintoken",
    serverTokenRevokationEndpoint: "http://www.domain.com/admintoken/revoke",
    refreshAccessTokens: true
  };

EDIT:

so by setting: in file: app/initializers/simple-auth-admin.js

import AuthenticatorBase from 'simple-auth-oauth2/authenticators/oauth2';
var CustomAuthenticator = AuthenticatorBase.extend({
    serverTokenEndpoint:            MyappENV['simple-auth-admin'].serverTokenEndpoint,
    serverTokenRevokationEndpoint:  MyappENV['simple-auth-admin'].serverRevokationTokenEndpoint,
    refreshAccessTokens:            MyappENV['simple-auth-admin'].refreshAccessTokens
});

console.log("AuthenticatorBase.serverTokenEndpoint =",AuthenticatorBase.serverTokenEndpoint);
console.log("CustomAuthenticator.serverTokenEndpoint =",CustomAuthenticator.serverTokenEndpoint);
console.log("MyappENV['simple-auth-admin'].serverTokenEndpoint =  ",MyappENV['simple-auth-admin'].serverTokenEndpoint);

export default {
    name:   'simple-auth-admin',
    before: 'simple-auth',
    initialize: function(container) {
        container.register('simple-auth-authenticator:admin', CustomAuthenticator);
        console.log("[at container.register] CustomAuthenticator.serverTokenEndpoint =  ",CustomAuthenticator.create().get('serverTokenEndpoint'));

    }
};

I get output of:

AuthenticatorBase.serverTokenEndpoint = undefined
CustomAuthenticator.serverTokenEndpoint = undefined
MyappENV['simple-auth-admin'].serverTokenEndpoint =  http://www.domain.com/oauth2/admintoken
[at container.register] CustomAuthenticator.serverTokenEndpoint = http://www.domain.com/oauth2/admintoken

Am I misuderstanding what AuthenticatorBase.extend () is doing? I thought it would allow you to override some variables or functions?

EDIT 2:

file: app/controllers/admin/login.js

import Ember from 'ember';
var $ = Ember.$;
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Controller.extend(LoginControllerMixin, {
    authenticator: 'simple-auth-authenticator:admin',
    init: function(){
        console.log('INIT LOGIN CONTROLLER', this.get('session'));
        this._super();
    },
    actions: {
        authenticate: function() { // (data)
            console.log("LoginController clicked");
            $('#nameBtn').ladda().ladda('start');
            console.log(this.get('session'));

            console.log('this.authenticator = ', this.authenticator);
            var _this = this;
            this._super().then(null, function(data) {
                console.log('LOGIN GOT BACK: ', data);
                $('#nameBtn').ladda().ladda('stop');
                    if(data.error !== undefined && data.error !== "") {
                    _this.set('data', {error: data.error});
                }
            });
        }
    }
});

This results in an ajax to www.domain.com/token rather than the expected www.domain.com/admintoken


Solution

  • OK, after a lot of coding in circles and trial and error and with a lot of help from:

    https://github.com/simplabs/ember-simple-auth/blob/master/examples/6-custom-server.html

    this is how I achieved what I wanted...

    1) Setup the enpoints as variables in the environment file (simple-auth-admin is the name i chose for my admin authenticator)

    File: /app/config/environment.js

    ENV['simple-auth-admin'] = {
        serverTokenEndpoint: "http://www.domain.com/admintoken",
        serverTokenRevokationEndpoint: "http://www.domain.com/admintoken/revoke",
        refreshAccessTokens: true
    };
    

    2) Create the actual authenticator as an override in an initialiser Note: in this case the CustomAuthorizer is not actually used and make sure you replace AppNameENV with your app name, so if your app was called bob it would be BobENV.

    File: /app/initializers/simple-auth-admin.js

    import Ember from 'ember';
    import AuthenticatorBase from 'simple-auth/authenticators/base';
    import AuthorizerBase from 'simple-auth/authorizers/base';
    
    var CustomAuthorizer = AuthorizerBase.extend({
    authorize: function(jqXHR, requestOptions) {
        if (this.get('session.isAuthenticated') && !Ember.isEmpty(this.get('session.token'))) {
            jqXHR.setRequestHeader('Authorization', 'Token: ' + this.get('session.token'));
        }
    }
    });
    
    var CustomAuthenticator = AuthenticatorBase.extend({
    tokenEndpoint: window.AppNameENV['simple-auth-admin'].serverTokenEndpoint,
    tokenRevokationEndpoint: window.AppNameENV['simple-auth-admin'].serverRevokationTokenEndpoint,
    refreshAccessTokens: window.AppNameENV['simple-auth-admin'].refreshAccessTokens,
    init: function(){
        console.log("CUSOTMM AUTH INIT ",window.AppNameENV['simple-auth-admin'].serverTokenEndpoint);
        this._super();
    },
    restore: function(data) {
        console.log('AdminAuth - restore');
        return new Ember.RSVP.Promise(function(resolve, reject) {
            if (!Ember.isEmpty(data.token)) {
                resolve(data);
            } else {
                reject();
            }
        });
    },
    authenticate: function(credentials) {
        console.log('AdminAuth - authenticate',credentials);
        var _this = this;
        return new Ember.RSVP.Promise(function(resolve, reject) {
            Ember.$.ajax({
                url: _this.tokenEndpoint,
                type: 'POST',
                data: JSON.stringify({ grant_type: 'password', username: credentials.identification, password: credentials.password, session: { identification: credentials.identification, password: credentials.password } }),
                        contentType: 'application/json'
                    }).then(function(response) {
                        Ember.run(function() {
                            resolve({ token: response.access_token });
                        });
                    }, function(xhr, status, error) {
                        var response = JSON.parse(xhr.responseText);
                        Ember.run(function() {
                            reject(response.error);
                        });
                    });
            });
        },
        invalidate: function() {
            console.log('AdminAuth - invalidate');
            var _this = this;
            return new Ember.RSVP.Promise(function(resolve) {
                Ember.$.ajax({ url: _this.tokenEndpoint, type: 'DELETE' }).always(function() {
                resolve();
                })
            });
        }
    });
    
    export default {
        name:   'simple-auth-admin',
        before: 'simple-auth',
        initialize: function(container) {
            console.log("OVERRIDES : ", window.AppNameENV['simple-auth-admin']);
            container.register('simple-auth-authenticator:admin', CustomAuthenticator);
            container.register('simple-auth-authorizer:admin', CustomAuthorizer);
        }
    };
    

    3) The I setup a redirect route to admin/login for any protected pages ( this example is for /admin/dashboard)

    File: /app/routes/admin/dashboard.js

    import Ember from 'ember';
    import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
    
    export default Ember.Route.extend(AuthenticatedRouteMixin,{
        authenticationRoute: 'admin.login',
        actions: {
            authenticateSession: function() {
                this.transitionTo(this.authenticationRoute);
            }
        }
    });
    

    4) Then configure the admin controller to use the new custom authenticator

    File: /app/controllers/admin/login.js

    import Ember from 'ember';
    var $ = Ember.$;
    import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
    //import Session from 'simple-auth/session';
    
    export default Ember.Controller.extend(LoginControllerMixin, {
        authenticator: 'simple-auth-authenticator:admin',
    });
    

    All of which seems a bit heavy handed when all i really wanted to do was have the authentication for the /admin/login point to a different serverendpoint. Marco, is there a way of overriding just those variables and therefore extend the simple-auth-oauth2 authorizer?