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 :
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.
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.
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.
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With