sahat / satellizer

Token-based AngularJS Authentication
https://satellizer-sahat.rhcloud.com
MIT License
7.85k stars 1.13k forks source link

How to configure $authProvider dynamically based on different environments? #259

Open rajhub opened 9 years ago

rajhub commented 9 years ago

Thanks for the great angular module for authentication. I've started using satellizer in my project.

We have multiple environments and each environment use different client ids for facebook, google, etc. In the client example you have these values are currently hardcoded in the app.js. I want these values to come from the server configuration. I'm using Node and Express on the backend.

I was thinking of creating a template file (say satellizer-config.tmpl) on the server for the authProvider configuration and add an route in the express configuration (say GET /satellizer-config). When the client GETs this URL the controller logic for this route will read the client ids from the server config file and substitute the parameters in the template and return the rendered output. I think I can use EJS for the rendering.

So in the index.html you will add the config as below. <script src="app.js"></script> <script src="satellizer-config"></script>

Would you recommend this approach? Or is there a better way to handle this? I think this is a common problem to solve and I'm sure many folks using your module would be facing this situation. Appreciate everyone's input here.

sahat commented 9 years ago

Hi @rajhub, I don't like the template approach, however, I don't have a good solution for this problem either. Currently I am manually swapping out client id and client secrets when testing Satellizer locally and when running it on Heroku (http://satellizer.herokuapp.com).

rajhub commented 9 years ago

Hi @sahat, how about returning environment specific file using res.sendFile() method?

Assume we have configuration file for each environment under config directory: config/satellizer-config.js.devevelopment config/satellizer-config.js.production

Then add a route something like this:

var satellizerConfFile = path.join(__dirname, 'config', satellizer-config.js.' + process.env.NODE_ENV);
app.get('/satellizer-config.js', function(req, res){res.sendFile(satellizerConfFile)});

Do you think this is better than template approach?

a14m commented 9 years ago

I think this apply to configuring angular js env not just satellizer. IMHO this is the best way to do it

Gillardo commented 8 years ago

I like the approach suggested by @artmees but this wont work in an enviroment like mine, which i believe is how @rajhub is setting up his application. My application, has 1 codebase, but i have multiple instances of this code on multiple servers. Each server will have its own providers to google/facebook etc, as i dont want 1 to be able to access another. I think something like this would be great #658

MtDalPizzol commented 8 years ago

How about the approach used by Restangular? You can configure the module using the RestangularProvider.setBaseUrl() in the .config() phase and the Restangular.setBaseUrl() service in the .run() phase.

MtDalPizzol commented 8 years ago

I tweaked the lib here so I can have an $auth.setBaseUrl() and an $auth.setLoginUrl(). It worked like a charm. So, I was able to config the module like this:

angular.module('myModule', []).run([function($auth, $location){
  if($location.host().indexOf('localhost') !== -1) {
    $auth.setBaseUrl('http://localhost/');
  } else {
    $auth.setBaseUrl('https://maydomain.com/');
  }
  $auth.setLoginUrl('/login');
}]);

If this is what you guys were looking for, I can make an effort to expose the other methods and make a PR...

Gillardo commented 8 years ago

Each method needs to be available on a per provider basis via the service. I dont think thre is an issue with setup in the run metgod via the current provider code On 3 Mar 2016 01:08, "MtDalPizzol" notifications@github.com wrote:

I tweeked the lib here so I can have an $auth.setBaseUrl() and an $auth.setLoginUrl(). It worked like a charm. So, I was able to config the module like this:

angular.module('myModule', []).run([function($auth, $location){ if($location.host().indexOf('localhost') !== -1) { $auth.setBaseUrl('http://localhost/'); } else { $auth.setBaseUrl('https://maydomain.com/'); } $auth.setLoginUrl('/login'); }]);

If this is what you guys were looking for, I can make an effort to expose the other methods and make a PR...

— Reply to this email directly or view it on GitHub https://github.com/sahat/satellizer/issues/259#issuecomment-191522941.

MtDalPizzol commented 8 years ago

Well... I don't know if I'm doing something wrong, but if I try to use $authProvider inside .run(), I get an Uncaught Error: [$injector:unpr] Unknown provider: $authProviderProvider <- $authProvider.

I also can't use $location inside .config(). So, what I suggested is what worked for me. Am I missing something?

Gillardo commented 8 years ago

You can only access the $authProvider from the .config method in your app. if you want to change the a property for a particular provider, you need to inject SatellizerConfig.

Here is a simple sample, of how to update the clientId for a provider that was setup in my .config method. So in my config method i have

// satellizer - for authentication
$authProvider.oauth2({
    name: 'openiddict',
    clientId: 'myClient',
    redirectUri: window.location.origin + '/signin-oidc',
    authorizationEndpoint: window.location.origin + '/connect/authorize',
    responseType: 'id_token token',
    scope: ['openid email profile roles'],
    requiredUrlParams: ['scope', 'nonce'],
    nonce: function () {
        var text = "";
        var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        for (var i = 0; i < 32; i++) {
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        }
        return text;
    },
    popupOptions: { width: 1028, height: 529 }
});

Then in my .run method, i can call

AppService.init();

And my AppService looks something like this

angular.module('app').service('AppService', ['$http', 'SatellizerConfig', function($http, SatellizerConfig) {
    'use strict';

    angular.extend(this, {
        init: function() {
            var that = this;

            $http.get('api/app/settings')
                .success(function(data) {
                    // set this here, so we dont have to do it in other places
                    SatellizerConfig.providers['openiddict'].clientId = data.clientId;
                });
        }
    });
}]);
MtDalPizzol commented 8 years ago

Awesome, it worked too! Thanx! This information should be in the docs!

tennisgent commented 7 years ago

I know I'm late to the game here, but couldn't we just add a method to the $auth provider to accept a clientId parameter before calling $auth.authenticate('...')?

$auth.setClientId('google', '<client_id>'); // set id based on the current environment
$auth.authenticate('google');

Is there a reason that the clientId has to be set during the config phase of the app lifecycle? Unless I'm missing something, it should be configurable at runtime, right?

mcblum commented 7 years ago

So glad I found this thread. Could the SatellizerConfig injectable be used to actually register a new provider? We have an app where we offer a bunch of difference integrations, but we'd like to simply tell the frontend about which integrations are available rather than having duplicate data.

mazoruss commented 6 years ago

SatellizerConfig worked for me as well, thanks! SatellizerConfig should definitely be in the readme tho.