Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authenticate Angular js module for Apigility

Tags:

I created an API using Apigility. I'm trying to set authentication system for a front end app that I'm currently building using the API.

But all angular authentication modules that I have used for this authentication system were not matching with Apigility oAuth 2 implementation :

  1. https://github.com/lynndylanhurley/ng-token-auth The problem with that module is, it doesn't allow CORS. However it allow to send a CORS-Request using a proxy on the server where angular code is located, which I have written in PHP using Guzzle. But with proxy ng-token-auth send a request twice succeed even if all authenticate data are false.

  2. https://github.com/sahat/satellizer This module need implementation of JWT but in the Apigility Authentication section I have not seen any documentation on it.

I need a help to finalize my project.

like image 496
hermannovich Avatar asked Feb 19 '15 01:02

hermannovich


2 Answers

I will try to give a complete method of how I have made ng-token-auth work with ZF2. Primarily, ng-token-auth works fine with ruby module. So to make it work with ZF2 :

Resolve the CORS problem with these lines of code :

//HttpProvider
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common['Access-Control-Request-Method'] = "POST, GET, PUT, DELETE";
$httpProvider.defaults.headers.common['Origin'] = "http://xxxxxxxxxxxxxxx";
$httpProvider.defaults.headers.common['Accept'] = "application/json";
$httpProvider.defaults.headers.common['Content-Type'] = "application/json; text/html";
delete $httpProvider.defaults.headers.common['X-Requested-With'];

Resolve the problem of CORS on ZF2 using ZFCORS as pointed in @josilber and @sven-lauterbach answer

Format response sent by ZF2 to make it work with ng-token-auth using these lines of codes

$http.defaults.transformResponse = function(value, headerGetters){
    var response_header = headerGetters(),
    response_data   = JsonHelper.IsJsonString(value) ? JSON.parse(value) : value;
    if(response_data){
        if(response_data.access_token)
            response_header['access_token']  = response_data.access_token;
        if(response_data.expires_in){
            var now = new Date().getTime();
            response_header['expires_in']    = now + ( parseInt(response_data.expires_in, 10) * 1000 );
        } 
        if(response_data.token_type)
            response_header['token_type']    = response_data.token_type;
        if(response_data.refresh_token)
            response_header['refresh_token'] = response_data.refresh_token;
        if(response_data.scope)
            response_header['scope']         = response_data.scope;
        return response_data;
    }
};

May be this is not the best way to transform response in AngularJS but it resolves the problem of formatting OAuth2 response which works with ng-token-auth

Finally, to send request to the server using auth token and refresh the token automatically, it was necessary to change some behavior of ng-token-auth. I have used decorate pattern on AngularJS to solve this issue with these snippets of code :

In app.js

//Change behavior of oauth2 module 
$provide.decorator("$auth", function($delegate, ApiAuthService){
    return ApiAuthService($delegate);
}); 

Where ApiAuthService is a factory defined by this snippet of code :

AuthProviderService.factory('ApiAuthService', ['MeService', function( MeService ){
    return function($delegate){
        return {
            initialize: function(){ return $delegate.initialize(); },
            apiUrl: function(configName){ },
            retrieveData: function(key){ return $delegate.retrieveData(key); },
            getConfig: function(name){ return $delegate.getConfig(name); },
            getExpiry: function(){  return $delegate.getExpiry(); },
            setAuthHeaders: function(h){ return $delegate.setAuthHeaders(h); },
            /*persistData: function(key, val, configName){ return $delegate.persistData(key, val, configName); },
            retrieveData: function(key){ return $delegate.retrieveData(key); },*/
            rejectDfd: function(reason){ $delegate.rejectDfd(reason); },
            invalidateTokens: function(){ return $delegate.invalidateTokens(); },
            submitLogin: function(params, opts){ return $delegate.submitLogin(params, opts); },
            validateUser: function(opts){  
                result = $delegate.validateUser(opts);
                return result;
            },
            deleteData: function(key){  
                return $delegate.deleteData(key);
            }
        };
    };
}]).config(['$httpProvider', function($httpProvider) {

    $httpProvider.interceptors.push([
         '$injector', function($injector) {
           return {
             request: function(req) {
               $injector.invoke([
                 '$http', '$auth', function($http, $auth) {
                   var key, 
                       _ref, 
                       _results = [];
                   if (req.url.match($auth.apiUrl())) {
                     _ref = $auth.retrieveData('auth_headers');
                     //Inject value into body of request 
                     for (key in _ref) {
                         //Set Authorization request header.
                         if(key.match('access_token')){
                             if(req.headers){
                                 req.headers['Authorization'] = 'Bearer ' + _ref[key]; 
                             }else{
                                 req.headers = {'Authorization': 'Bearer ' + _ref[key]};
                             }
                         }
                         if(req.headers[key]){
                             delete req.headers[key];
                         }
                     }
                     return _results;
                   }
                 }
               ]);
               return req;
             }
           };
         }
       ]);
}]);

Lastly my configuration of ng-token-auth was :

//OAuth2 Module configs
$authProvider.configure([ {
    "default": {
        apiUrl:                  API_URL,
        tokenValidationPath:     '/me',
        signOutUrl:              '/oauth',
        emailRegistrationPath:   '/oauth',
        accountUpdatePath:       '/oauth',
        accountDeletePath:       '/oauth',
        confirmationSuccessUrl:  window.location.href,
        passwordResetPath:       '/oauth',
        passwordUpdatePath:      '/oauth',
        passwordResetSuccessUrl: window.location.href,
        emailSignInPath:         '/oauth',
        forceHardRedirect: true,
        storage:                 'localStorage',
        proxyIf:                 function() { return false; },
        proxyUrl:                'proxy',
        authProviderPaths: {
            github:   '/auth/github',
            facebook: '/auth/facebook',
            google:   '/auth/google'
        },
        tokenFormat: {
            "access_token" : "{{ token }}",
            "token_type"   : "Bearer",
            "refresh_token": "{{ clientId }}",
            "expires_in"   : "{{ expiry }}",
            "scope"        : "{{ uid }}"
        },
        parseExpiry: function(headers) {
            var expires_in = parseInt(headers['expires_in'], 10) || null;
                return expires_in;
            },
            handleLoginResponse: function(response) {
                //Patch for persistant data as library retreive auth data from header.
                return response;
            },
            handleAccountResponse: function(response) {
                return response;
            },
            handleTokenValidationResponse: function(response) {
                return response;
            }
        }
} ]);

@JerinKAlexander I hope these steps will help you to find your way to solve your question in a better way than what I have done.

like image 88
hermannovich Avatar answered Dec 15 '22 15:12

hermannovich


You can actually get satellizer to work with Apigility using a rather simple but neat workaround. Take a look here :

http://adam.lundrigan.ca/2014/11/06/using-oauth2-jwt-with-apigility/

and here:

https://github.com/adamlundrigan/LdcOAuth2CryptoToken/blob/master/src/Factory/CryptoTokenServerFactory.php

Apigility defines service factories for all it's internal services. The basic idea here is to simply define a service manager delegator factory which injects the necessary configuration.

<?php  
namespace LdcOAuth2CryptoToken\Factory;

use Zend\ServiceManager\DelegatorFactoryInterface;  
use Zend\ServiceManager\ServiceLocatorInterface;
class CryptoTokenServerFactory implements DelegatorFactoryInterface  
{
    public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback)
    {
        $server = call_user_func($callback);

        // do your thing to $server here

        return $server;
    }
}

All thanks to Adam Lundrigan :)

like image 23
jester Avatar answered Dec 15 '22 15:12

jester