2
votes

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

3

3 Answers

1
votes

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?

1
votes

The routes you're defining are necessary of course.

Since your authorizer and authenticator for the admin area seem to be customized as well those are necessary as well of course. If you used the plain OAuth 2.0 ones for the admin area as well you could drop the authorizer and change the authenticator to

import AuthenticatorBase from 'simple-auth-oauth2/authenticators/oauth2';

var CustomAuthenticator = AuthenticatorBase.extend({
  serverTokenEndpoint:           'http://www.domain.com/admintoken',
  serverTokenRevokationEndpoint: 'http://www.domain.com/admintoken/revoke'
});
0
votes

Every time Ember Simple Auth enforces authentication (usually when a user accesses an authenticated route while the session is not authenticated), it calls the ApplicationRouteMixin's authenticateSession action. The best option you have is to override that and somehow decide whether to transition to the admin or the guest login page from there. If you have e.g. your admin pages namespaces in an /admin route, you could also override the authenticateSession on the AdminRoute and transition to the admin login page from there while the default implementation in the ApplicationRoute transitions to the guest login page.

For the authenticators it's probably best to use the default OAuth 2.0 authenticator with its serverTokenEndpoint to authenticate guests and extend another authenticator from that that authenticates admin against a different serverTokenEndpoint.