From 4f5d88fb8a14c5a7a371a7975fe8c08110fc725f Mon Sep 17 00:00:00 2001 From: amullins83 Date: Fri, 13 Mar 2015 17:08:52 -0500 Subject: [PATCH] Add parameter to login method to allow dynamic redirect An extra parameter on the `$auth.login` method allows redirect URLs to be determined in runtime. --- examples/client/vendor/satellizer.js | 30 +++++++++----- satellizer.js | 15 ++++--- satellizer.min.js | 2 +- test/auth.spec.js | 23 +++++++++++ test/local.spec.js | 24 +++++++++++ test/shared.spec.js | 60 ++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 16 deletions(-) diff --git a/examples/client/vendor/satellizer.js b/examples/client/vendor/satellizer.js index ad1c44d9..3b1c65a5 100644 --- a/examples/client/vendor/satellizer.js +++ b/examples/client/vendor/satellizer.js @@ -210,8 +210,8 @@ return oauth.authenticate(name, false, userData); }; - $auth.login = function(user) { - return local.login(user); + $auth.login = function(user, redirect) { + return local.login(user, redirect); }; $auth.signup = function(user) { @@ -278,7 +278,7 @@ } }; - shared.setToken = function(response, isLinking) { + shared.setToken = function(response, redirect) { var accessToken = response && response.access_token; var token; @@ -304,9 +304,12 @@ $window.localStorage[tokenName] = token; - if (config.loginRedirect && !isLinking) { + if (config.loginRedirect && !redirect) { $location.path(config.loginRedirect); } + else if (redirect && angular.isString(redirect)) { + $location.path(encodeURI(redirect)); + } }; shared.removeToken = function() { @@ -390,10 +393,10 @@ function($q, $http, $location, utils, shared, config) { var local = {}; - local.login = function(user) { + local.login = function(user, redirect) { return $http.post(config.loginUrl, user) .then(function(response) { - shared.setToken(response); + shared.setToken(response, redirect); return response; }); }; @@ -525,14 +528,15 @@ var defaults = { url: null, name: null, - popupOptions: null + popupOptions: null, + redirectUri: null }; var oauth1 = {}; oauth1.open = function(options, userData) { angular.extend(defaults, options); - return popup.open(defaults.url, defaults.popupOptions) + return popup.open(defaults.url, defaults.popupOptions, defaults.redirectUri) .then(function(response) { return oauth1.exchangeForToken(response, userData); }); @@ -597,6 +601,7 @@ var parser = document.createElement('a'); parser.href = event.url; + if(parser.search || parser.hash){ var queryParams = parser.search.substring(1).replace(/\/$/, ''); var hashParams = parser.hash.substring(1).replace(/\/$/, ''); var hash = utils.parseQueryString(hashParams); @@ -607,10 +612,17 @@ if (qs.error) { deferred.reject({ error: qs.error }); } else { - deferred.resolve({ code: qs.code }); + deferred.resolve(qs); } popupWindow.close(); + } + }); + popupWindow.addEventListener('exit', function() { + deferred.reject({data: 'Provider Popup was closed'}); + }); + popupWindow.addEventListener('loaderror', function() { + deferred.reject({data: 'Authorization Failed'}); }); return deferred.promise; diff --git a/satellizer.js b/satellizer.js index 74584429..3b1c65a5 100644 --- a/satellizer.js +++ b/satellizer.js @@ -210,8 +210,8 @@ return oauth.authenticate(name, false, userData); }; - $auth.login = function(user) { - return local.login(user); + $auth.login = function(user, redirect) { + return local.login(user, redirect); }; $auth.signup = function(user) { @@ -278,7 +278,7 @@ } }; - shared.setToken = function(response, isLinking) { + shared.setToken = function(response, redirect) { var accessToken = response && response.access_token; var token; @@ -304,9 +304,12 @@ $window.localStorage[tokenName] = token; - if (config.loginRedirect && !isLinking) { + if (config.loginRedirect && !redirect) { $location.path(config.loginRedirect); } + else if (redirect && angular.isString(redirect)) { + $location.path(encodeURI(redirect)); + } }; shared.removeToken = function() { @@ -390,10 +393,10 @@ function($q, $http, $location, utils, shared, config) { var local = {}; - local.login = function(user) { + local.login = function(user, redirect) { return $http.post(config.loginUrl, user) .then(function(response) { - shared.setToken(response); + shared.setToken(response, redirect); return response; }); }; diff --git a/satellizer.min.js b/satellizer.min.js index 602fcb62..7f40d791 100644 --- a/satellizer.min.js +++ b/satellizer.min.js @@ -3,4 +3,4 @@ * (c) 2015 Sahat Yalkabov * License: MIT */ -!function(e,t){"use strict";t.module("satellizer",[]).constant("satellizer.config",{httpInterceptor:!0,loginOnSignup:!0,loginRedirect:"/",logoutRedirect:"/",signupRedirect:"/login",loginUrl:"/auth/login",signupUrl:"/auth/signup",loginRoute:"/login",signupRoute:"/signup",tokenRoot:!1,tokenName:"token",tokenPrefix:"satellizer",unlinkUrl:"/auth/unlink/",unlinkMethod:"get",authHeader:"Authorization",withCredentials:!0,platform:"browser",providers:{google:{name:"google",url:"/auth/google",authorizationEndpoint:"https://accounts.google.com/o/oauth2/auth",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,scope:["profile","email"],scopePrefix:"openid",scopeDelimiter:" ",requiredUrlParams:["scope"],optionalUrlParams:["display"],display:"popup",type:"2.0",popupOptions:{width:452,height:633}},facebook:{name:"facebook",url:"/auth/facebook",authorizationEndpoint:"https://www.facebook.com/dialog/oauth",redirectUri:e.location.origin+"/"||e.location.protocol+"//"+e.location.host+"/",scope:["email"],scopeDelimiter:",",requiredUrlParams:["display","scope"],display:"popup",type:"2.0",popupOptions:{width:580,height:400}},linkedin:{name:"linkedin",url:"/auth/linkedin",authorizationEndpoint:"https://www.linkedin.com/uas/oauth2/authorization",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,requiredUrlParams:["state"],scope:["r_emailaddress"],scopeDelimiter:" ",state:"STATE",type:"2.0",popupOptions:{width:527,height:582}},github:{name:"github",url:"/auth/github",authorizationEndpoint:"https://github.com/login/oauth/authorize",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,optionalUrlParams:["scope"],scope:["user:email"],scopeDelimiter:" ",type:"2.0",popupOptions:{width:1020,height:618}},yahoo:{name:"yahoo",url:"/auth/yahoo",authorizationEndpoint:"https://api.login.yahoo.com/oauth2/request_auth",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,scope:[],scopeDelimiter:",",type:"2.0",popupOptions:{width:559,height:519}},twitter:{name:"twitter",url:"/auth/twitter",type:"1.0",popupOptions:{width:495,height:645}},live:{name:"live",url:"/auth/live",authorizationEndpoint:"https://login.live.com/oauth20_authorize.srf",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,scope:["wl.emails"],scopeDelimiter:" ",requiredUrlParams:["display","scope"],display:"popup",type:"2.0",popupOptions:{width:500,height:560}}}}).provider("$auth",["satellizer.config",function(e){Object.defineProperties(this,{loginOnSignup:{get:function(){return e.loginOnSignup},set:function(t){e.loginOnSignup=t}},httpInterceptor:{get:function(){return e.httpInterceptor},set:function(t){e.httpInterceptor=t}},logoutRedirect:{get:function(){return e.logoutRedirect},set:function(t){e.logoutRedirect=t}},loginRedirect:{set:function(t){e.loginRedirect=t},get:function(){return e.loginRedirect}},signupRedirect:{get:function(){return e.signupRedirect},set:function(t){e.signupRedirect=t}},loginUrl:{get:function(){return e.loginUrl},set:function(t){e.loginUrl=t}},signupUrl:{get:function(){return e.signupUrl},set:function(t){e.signupUrl=t}},loginRoute:{get:function(){return e.loginRoute},set:function(t){e.loginRoute=t}},signupRoute:{get:function(){return e.signupRoute},set:function(t){e.signupRoute=t}},tokenRoot:{get:function(){return e.tokenRoot},set:function(t){e.tokenRoot=t}},tokenName:{get:function(){return e.tokenName},set:function(t){e.tokenName=t}},tokenPrefix:{get:function(){return e.tokenPrefix},set:function(t){e.tokenPrefix=t}},unlinkUrl:{get:function(){return e.unlinkUrl},set:function(t){e.unlinkUrl=t}},authHeader:{get:function(){return e.authHeader},set:function(t){e.authHeader=t}},withCredentials:{get:function(){return e.withCredentials},set:function(t){e.withCredentials=t}},unlinkMethod:{get:function(){return e.unlinkMethod},set:function(t){e.unlinkMethod=t}},platform:{get:function(){return e.platform},set:function(t){e.platform=t}}}),t.forEach(Object.keys(e.providers),function(n){this[n]=function(o){return t.extend(e.providers[n],o)}},this);var n=function(n){e.providers[n.name]=e.providers[n.name]||{},t.extend(e.providers[n.name],n)};this.oauth1=function(t){n(t),e.providers[t.name].type="1.0"},this.oauth2=function(t){n(t),e.providers[t.name].type="2.0"},this.$get=["$q","satellizer.shared","satellizer.local","satellizer.oauth",function(e,t,n,o){var r={};return r.authenticate=function(e,t){return o.authenticate(e,!1,t)},r.login=function(e){return n.login(e)},r.signup=function(e){return n.signup(e)},r.logout=function(){return t.logout()},r.isAuthenticated=function(){return t.isAuthenticated()},r.link=function(e,t){return o.authenticate(e,!0,t)},r.unlink=function(e){return o.unlink(e)},r.getToken=function(){return t.getToken()},r.setToken=function(e,n){t.setToken({access_token:e},n)},r.removeToken=function(){return t.removeToken()},r.getPayload=function(){return t.getPayload()},r}]}]).factory("satellizer.shared",["$q","$window","$location","satellizer.config",function(e,n,o,r){var i={};return i.getToken=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;return n.localStorage[e]},i.getPayload=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName,t=n.localStorage[e];if(t&&3===t.split(".").length){var o=t.split(".")[1],i=o.replace("-","+").replace("_","/");return JSON.parse(n.atob(i))}},i.setToken=function(e,i){var a,u=e&&e.access_token;u&&(t.isObject(u)&&t.isObject(u.data)?e=u:t.isString(u)&&(a=u)),!a&&e&&(a=r.tokenRoot&&e.data[r.tokenRoot]?e.data[r.tokenRoot][r.tokenName]:e.data[r.tokenName]);var l=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;if(!a)throw l=r.tokenRoot?r.tokenRoot+"."+r.tokenName:r.tokenName,new Error('Expecting a token named "'+l+'" but instead got: '+JSON.stringify(e.data));n.localStorage[l]=a,r.loginRedirect&&!i&&o.path(r.loginRedirect)},i.removeToken=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;delete n.localStorage[e]},i.isAuthenticated=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName,t=n.localStorage[e];if(t){if(3===t.split(".").length){var o=t.split(".")[1],i=o.replace("-","+").replace("_","/"),a=JSON.parse(n.atob(i)).exp;if(a)return Math.round((new Date).getTime()/1e3)<=a}return!0}return!1},i.logout=function(){var t=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;return delete n.localStorage[t],r.logoutRedirect&&o.url(r.logoutRedirect),e.when()},i}]).factory("satellizer.oauth",["$q","$http","satellizer.config","satellizer.shared","satellizer.Oauth1","satellizer.Oauth2",function(e,t,n,o,r,i){var a={};return a.authenticate=function(t,a,u){var l="1.0"===n.providers[t].type?new r:new i,c=e.defer();return l.open(n.providers[t],u||{}).then(function(e){o.setToken(e,a),c.resolve(e)}).catch(function(e){c.reject(e)}),c.promise},a.unlink=function(e){return"get"===n.unlinkMethod?t.get(n.unlinkUrl+e):"post"===n.unlinkMethod?t.post(n.unlinkUrl,e):void 0},a}]).factory("satellizer.local",["$q","$http","$location","satellizer.utils","satellizer.shared","satellizer.config",function(e,t,n,o,r,i){var a={};return a.login=function(e){return t.post(i.loginUrl,e).then(function(e){return r.setToken(e),e})},a.signup=function(e){return t.post(i.signupUrl,e).then(function(e){return i.loginOnSignup?r.setToken(e):i.signupRedirect&&n.path(i.signupRedirect),e})},a}]).factory("satellizer.Oauth2",["$q","$http","$window","satellizer.popup","satellizer.utils","satellizer.config",function(e,n,o,r,i,a){return function(){var u={url:null,name:null,state:null,scope:null,scopeDelimiter:null,clientId:null,redirectUri:null,popupOptions:null,authorizationEndpoint:null,responseParams:null,requiredUrlParams:null,optionalUrlParams:null,defaultUrlParams:["response_type","client_id","redirect_uri"],responseType:"code"},l={};return l.open=function(n,i){t.extend(u,n);var a=u.name+"_state";t.isFunction(u.state)?o.localStorage[a]=u.state():t.isString(u.state)&&(o.localStorage[a]=u.state);var c=u.authorizationEndpoint+"?"+l.buildQueryString();return r.open(c,u.popupOptions,u.redirectUri).then(function(t){return"token"===u.responseType?t:t.state&&t.state!==o.localStorage[a]?e.reject("OAuth 2.0 state parameter mismatch."):l.exchangeForToken(t,i)})},l.exchangeForToken=function(e,o){var r=t.extend({},o,{code:e.code,clientId:u.clientId,redirectUri:u.redirectUri});return e.state&&(r.state=e.state),t.forEach(u.responseParams,function(t){r[t]=e[t]}),n.post(u.url,r,{withCredentials:a.withCredentials})},l.buildQueryString=function(){var e=[],n=["defaultUrlParams","requiredUrlParams","optionalUrlParams"];return t.forEach(n,function(n){t.forEach(u[n],function(t){var n=i.camelCase(t),r=u[n];if("state"===t){var a=u.name+"_state";r=o.localStorage[a]}"scope"===t&&Array.isArray(r)&&(r=r.join(u.scopeDelimiter),u.scopePrefix&&(r=[u.scopePrefix,r].join(u.scopeDelimiter))),e.push([t,r])})}),e.map(function(e){return e.join("=")}).join("&")},l}}]).factory("satellizer.Oauth1",["$q","$http","satellizer.popup",function(e,n,o){return function(){var e={url:null,name:null,popupOptions:null},r={};return r.open=function(n,i){return t.extend(e,n),o.open(e.url,e.popupOptions).then(function(e){return r.exchangeForToken(e,i)})},r.exchangeForToken=function(o,i){var a=t.extend({},i,o),u=r.buildQueryString(a);return n.get(e.url+"?"+u)},r.buildQueryString=function(e){var n=[];return t.forEach(e,function(e,t){n.push(encodeURIComponent(t)+"="+encodeURIComponent(e))}),n.join("&")},r}}]).factory("satellizer.popup",["$q","$interval","$window","$location","satellizer.config","satellizer.utils",function(n,o,r,i,a,u){var l=null,c=null,s={};return s.popupWindow=l,s.open=function(t,n,o){var r=s.stringifyOptions(s.prepareOptions(n||{}));return l=e.open(t,"_blank",r),l&&l.focus&&l.focus(),"mobile"===a.platform?s.eventListener(o):s.pollPopup()},s.eventListener=function(e){var o=n.defer();return l.addEventListener("loadstart",function(n){if(0===n.url.indexOf(e)){var r=document.createElement("a");r.href=n.url;var i=r.search.substring(1).replace(/\/$/,""),a=r.hash.substring(1).replace(/\/$/,""),c=u.parseQueryString(a),s=u.parseQueryString(i);t.extend(s,c),s.error?o.reject({error:s.error}):o.resolve({code:s.code}),l.close()}}),o.promise},s.pollPopup=function(){var e=n.defer();return c=o(function(){try{if(l.document.domain===document.domain&&(l.location.search||l.location.hash)){var n=l.location.search.substring(1).replace(/\/$/,""),r=l.location.hash.substring(1).replace(/\/$/,""),i=u.parseQueryString(r),a=u.parseQueryString(n);t.extend(a,i),a.error?e.reject({error:a.error}):e.resolve(a),l.close(),o.cancel(c)}}catch(s){}l?l.closed&&(o.cancel(c),e.reject({data:"Authorization Failed"})):(o.cancel(c),e.reject({data:"Provider Popup Blocked"}))},35),e.promise},s.prepareOptions=function(e){var n=e.width||500,o=e.height||500;return t.extend({width:n,height:o,left:r.screenX+(r.outerWidth-n)/2,top:r.screenY+(r.outerHeight-o)/2.5},e)},s.stringifyOptions=function(e){var n=[];return t.forEach(e,function(e,t){n.push(t+"="+e)}),n.join(",")},s}]).service("satellizer.utils",function(){this.camelCase=function(e){return e.replace(/([\:\-\_]+(.))/g,function(e,t,n,o){return o?n.toUpperCase():n})},this.parseQueryString=function(e){var n,o,r={};return t.forEach((e||"").split("&"),function(e){e&&(o=e.split("="),n=decodeURIComponent(o[0]),r[n]=t.isDefined(o[1])?decodeURIComponent(o[1]):!0)}),r}}).config(["$httpProvider","satellizer.config",function(e,t){e.interceptors.push(["$q",function(e){var n=t.tokenPrefix?t.tokenPrefix+"_"+t.tokenName:t.tokenName;return{request:function(e){var o=localStorage.getItem(n);return o&&t.httpInterceptor&&(o="Authorization"===t.authHeader?"Bearer "+o:o,e.headers[t.authHeader]=o),e},responseError:function(t){return e.reject(t)}}}])}])}(window,window.angular),function(){function e(e){this.message=e}var t="undefined"!=typeof exports?exports:this,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";e.prototype=new Error,e.prototype.name="InvalidCharacterError",t.btoa||(t.btoa=function(t){for(var o,r,i=String(t),a=0,u=n,l="";i.charAt(0|a)||(u="=",a%1);l+=u.charAt(63&o>>8-a%1*8)){if(r=i.charCodeAt(a+=.75),r>255)throw new e("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|r}return l}),t.atob||(t.atob=function(t){var o=String(t).replace(/=+$/,"");if(o.length%4==1)throw new e("'atob' failed: The string to be decoded is not correctly encoded.");for(var r,i,a=0,u=0,l="";i=o.charAt(u++);~i&&(r=a%4?64*r+i:i,a++%4)?l+=String.fromCharCode(255&r>>(-2*a&6)):0)i=n.indexOf(i);return l})}(); \ No newline at end of file +!function(e,t){"use strict";t.module("satellizer",[]).constant("satellizer.config",{httpInterceptor:!0,loginOnSignup:!0,loginRedirect:"/",logoutRedirect:"/",signupRedirect:"/login",loginUrl:"/auth/login",signupUrl:"/auth/signup",loginRoute:"/login",signupRoute:"/signup",tokenRoot:!1,tokenName:"token",tokenPrefix:"satellizer",unlinkUrl:"/auth/unlink/",unlinkMethod:"get",authHeader:"Authorization",withCredentials:!0,platform:"browser",providers:{google:{name:"google",url:"/auth/google",authorizationEndpoint:"https://accounts.google.com/o/oauth2/auth",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,scope:["profile","email"],scopePrefix:"openid",scopeDelimiter:" ",requiredUrlParams:["scope"],optionalUrlParams:["display"],display:"popup",type:"2.0",popupOptions:{width:452,height:633}},facebook:{name:"facebook",url:"/auth/facebook",authorizationEndpoint:"https://www.facebook.com/dialog/oauth",redirectUri:e.location.origin+"/"||e.location.protocol+"//"+e.location.host+"/",scope:["email"],scopeDelimiter:",",requiredUrlParams:["display","scope"],display:"popup",type:"2.0",popupOptions:{width:580,height:400}},linkedin:{name:"linkedin",url:"/auth/linkedin",authorizationEndpoint:"https://www.linkedin.com/uas/oauth2/authorization",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,requiredUrlParams:["state"],scope:["r_emailaddress"],scopeDelimiter:" ",state:"STATE",type:"2.0",popupOptions:{width:527,height:582}},github:{name:"github",url:"/auth/github",authorizationEndpoint:"https://github.com/login/oauth/authorize",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,optionalUrlParams:["scope"],scope:["user:email"],scopeDelimiter:" ",type:"2.0",popupOptions:{width:1020,height:618}},yahoo:{name:"yahoo",url:"/auth/yahoo",authorizationEndpoint:"https://api.login.yahoo.com/oauth2/request_auth",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,scope:[],scopeDelimiter:",",type:"2.0",popupOptions:{width:559,height:519}},twitter:{name:"twitter",url:"/auth/twitter",type:"1.0",popupOptions:{width:495,height:645}},live:{name:"live",url:"/auth/live",authorizationEndpoint:"https://login.live.com/oauth20_authorize.srf",redirectUri:e.location.origin||e.location.protocol+"//"+e.location.host,scope:["wl.emails"],scopeDelimiter:" ",requiredUrlParams:["display","scope"],display:"popup",type:"2.0",popupOptions:{width:500,height:560}}}}).provider("$auth",["satellizer.config",function(e){Object.defineProperties(this,{loginOnSignup:{get:function(){return e.loginOnSignup},set:function(t){e.loginOnSignup=t}},httpInterceptor:{get:function(){return e.httpInterceptor},set:function(t){e.httpInterceptor=t}},logoutRedirect:{get:function(){return e.logoutRedirect},set:function(t){e.logoutRedirect=t}},loginRedirect:{set:function(t){e.loginRedirect=t},get:function(){return e.loginRedirect}},signupRedirect:{get:function(){return e.signupRedirect},set:function(t){e.signupRedirect=t}},loginUrl:{get:function(){return e.loginUrl},set:function(t){e.loginUrl=t}},signupUrl:{get:function(){return e.signupUrl},set:function(t){e.signupUrl=t}},loginRoute:{get:function(){return e.loginRoute},set:function(t){e.loginRoute=t}},signupRoute:{get:function(){return e.signupRoute},set:function(t){e.signupRoute=t}},tokenRoot:{get:function(){return e.tokenRoot},set:function(t){e.tokenRoot=t}},tokenName:{get:function(){return e.tokenName},set:function(t){e.tokenName=t}},tokenPrefix:{get:function(){return e.tokenPrefix},set:function(t){e.tokenPrefix=t}},unlinkUrl:{get:function(){return e.unlinkUrl},set:function(t){e.unlinkUrl=t}},authHeader:{get:function(){return e.authHeader},set:function(t){e.authHeader=t}},withCredentials:{get:function(){return e.withCredentials},set:function(t){e.withCredentials=t}},unlinkMethod:{get:function(){return e.unlinkMethod},set:function(t){e.unlinkMethod=t}},platform:{get:function(){return e.platform},set:function(t){e.platform=t}}}),t.forEach(Object.keys(e.providers),function(n){this[n]=function(o){return t.extend(e.providers[n],o)}},this);var n=function(n){e.providers[n.name]=e.providers[n.name]||{},t.extend(e.providers[n.name],n)};this.oauth1=function(t){n(t),e.providers[t.name].type="1.0"},this.oauth2=function(t){n(t),e.providers[t.name].type="2.0"},this.$get=["$q","satellizer.shared","satellizer.local","satellizer.oauth",function(e,t,n,o){var r={};return r.authenticate=function(e,t){return o.authenticate(e,!1,t)},r.login=function(e,t){return n.login(e,t)},r.signup=function(e){return n.signup(e)},r.logout=function(){return t.logout()},r.isAuthenticated=function(){return t.isAuthenticated()},r.link=function(e,t){return o.authenticate(e,!0,t)},r.unlink=function(e){return o.unlink(e)},r.getToken=function(){return t.getToken()},r.setToken=function(e,n){t.setToken({access_token:e},n)},r.removeToken=function(){return t.removeToken()},r.getPayload=function(){return t.getPayload()},r}]}]).factory("satellizer.shared",["$q","$window","$location","satellizer.config",function(e,n,o,r){var i={};return i.getToken=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;return n.localStorage[e]},i.getPayload=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName,t=n.localStorage[e];if(t&&3===t.split(".").length){var o=t.split(".")[1],i=o.replace("-","+").replace("_","/");return JSON.parse(n.atob(i))}},i.setToken=function(e,i){var a,u=e&&e.access_token;u&&(t.isObject(u)&&t.isObject(u.data)?e=u:t.isString(u)&&(a=u)),!a&&e&&(a=r.tokenRoot&&e.data[r.tokenRoot]?e.data[r.tokenRoot][r.tokenName]:e.data[r.tokenName]);var l=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;if(!a)throw l=r.tokenRoot?r.tokenRoot+"."+r.tokenName:r.tokenName,new Error('Expecting a token named "'+l+'" but instead got: '+JSON.stringify(e.data));n.localStorage[l]=a,r.loginRedirect&&!i?o.path(r.loginRedirect):i&&t.isString(i)&&o.path(encodeURI(i))},i.removeToken=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;delete n.localStorage[e]},i.isAuthenticated=function(){var e=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName,t=n.localStorage[e];if(t){if(3===t.split(".").length){var o=t.split(".")[1],i=o.replace("-","+").replace("_","/"),a=JSON.parse(n.atob(i)).exp;if(a)return Math.round((new Date).getTime()/1e3)<=a}return!0}return!1},i.logout=function(){var t=r.tokenPrefix?r.tokenPrefix+"_"+r.tokenName:r.tokenName;return delete n.localStorage[t],r.logoutRedirect&&o.url(r.logoutRedirect),e.when()},i}]).factory("satellizer.oauth",["$q","$http","satellizer.config","satellizer.shared","satellizer.Oauth1","satellizer.Oauth2",function(e,t,n,o,r,i){var a={};return a.authenticate=function(t,a,u){var l="1.0"===n.providers[t].type?new r:new i,c=e.defer();return l.open(n.providers[t],u||{}).then(function(e){o.setToken(e,a),c.resolve(e)}).catch(function(e){c.reject(e)}),c.promise},a.unlink=function(e){return"get"===n.unlinkMethod?t.get(n.unlinkUrl+e):"post"===n.unlinkMethod?t.post(n.unlinkUrl,e):void 0},a}]).factory("satellizer.local",["$q","$http","$location","satellizer.utils","satellizer.shared","satellizer.config",function(e,t,n,o,r,i){var a={};return a.login=function(e,n){return t.post(i.loginUrl,e).then(function(e){return r.setToken(e,n),e})},a.signup=function(e){return t.post(i.signupUrl,e).then(function(e){return i.loginOnSignup?r.setToken(e):i.signupRedirect&&n.path(i.signupRedirect),e})},a}]).factory("satellizer.Oauth2",["$q","$http","$window","satellizer.popup","satellizer.utils","satellizer.config",function(e,n,o,r,i,a){return function(){var u={url:null,name:null,state:null,scope:null,scopeDelimiter:null,clientId:null,redirectUri:null,popupOptions:null,authorizationEndpoint:null,responseParams:null,requiredUrlParams:null,optionalUrlParams:null,defaultUrlParams:["response_type","client_id","redirect_uri"],responseType:"code"},l={};return l.open=function(n,i){t.extend(u,n);var a=u.name+"_state";t.isFunction(u.state)?o.localStorage[a]=u.state():t.isString(u.state)&&(o.localStorage[a]=u.state);var c=u.authorizationEndpoint+"?"+l.buildQueryString();return r.open(c,u.popupOptions,u.redirectUri).then(function(t){return"token"===u.responseType?t:t.state&&t.state!==o.localStorage[a]?e.reject("OAuth 2.0 state parameter mismatch."):l.exchangeForToken(t,i)})},l.exchangeForToken=function(e,o){var r=t.extend({},o,{code:e.code,clientId:u.clientId,redirectUri:u.redirectUri});return e.state&&(r.state=e.state),t.forEach(u.responseParams,function(t){r[t]=e[t]}),n.post(u.url,r,{withCredentials:a.withCredentials})},l.buildQueryString=function(){var e=[],n=["defaultUrlParams","requiredUrlParams","optionalUrlParams"];return t.forEach(n,function(n){t.forEach(u[n],function(t){var n=i.camelCase(t),r=u[n];if("state"===t){var a=u.name+"_state";r=o.localStorage[a]}"scope"===t&&Array.isArray(r)&&(r=r.join(u.scopeDelimiter),u.scopePrefix&&(r=[u.scopePrefix,r].join(u.scopeDelimiter))),e.push([t,r])})}),e.map(function(e){return e.join("=")}).join("&")},l}}]).factory("satellizer.Oauth1",["$q","$http","satellizer.popup",function(e,n,o){return function(){var e={url:null,name:null,popupOptions:null,redirectUri:null},r={};return r.open=function(n,i){return t.extend(e,n),o.open(e.url,e.popupOptions,e.redirectUri).then(function(e){return r.exchangeForToken(e,i)})},r.exchangeForToken=function(o,i){var a=t.extend({},i,o),u=r.buildQueryString(a);return n.get(e.url+"?"+u)},r.buildQueryString=function(e){var n=[];return t.forEach(e,function(e,t){n.push(encodeURIComponent(t)+"="+encodeURIComponent(e))}),n.join("&")},r}}]).factory("satellizer.popup",["$q","$interval","$window","$location","satellizer.config","satellizer.utils",function(n,o,r,i,a,u){var l=null,c=null,s={};return s.popupWindow=l,s.open=function(t,n,o){var r=s.stringifyOptions(s.prepareOptions(n||{}));return l=e.open(t,"_blank",r),l&&l.focus&&l.focus(),"mobile"===a.platform?s.eventListener(o):s.pollPopup()},s.eventListener=function(e){var o=n.defer();return l.addEventListener("loadstart",function(n){if(0===n.url.indexOf(e)){var r=document.createElement("a");if(r.href=n.url,r.search||r.hash){var i=r.search.substring(1).replace(/\/$/,""),a=r.hash.substring(1).replace(/\/$/,""),c=u.parseQueryString(a),s=u.parseQueryString(i);t.extend(s,c),s.error?o.reject({error:s.error}):o.resolve(s),l.close()}}}),l.addEventListener("exit",function(){o.reject({data:"Provider Popup was closed"})}),l.addEventListener("loaderror",function(){o.reject({data:"Authorization Failed"})}),o.promise},s.pollPopup=function(){var e=n.defer();return c=o(function(){try{if(l.document.domain===document.domain&&(l.location.search||l.location.hash)){var n=l.location.search.substring(1).replace(/\/$/,""),r=l.location.hash.substring(1).replace(/\/$/,""),i=u.parseQueryString(r),a=u.parseQueryString(n);t.extend(a,i),a.error?e.reject({error:a.error}):e.resolve(a),l.close(),o.cancel(c)}}catch(s){}l?l.closed&&(o.cancel(c),e.reject({data:"Authorization Failed"})):(o.cancel(c),e.reject({data:"Provider Popup Blocked"}))},35),e.promise},s.prepareOptions=function(e){var n=e.width||500,o=e.height||500;return t.extend({width:n,height:o,left:r.screenX+(r.outerWidth-n)/2,top:r.screenY+(r.outerHeight-o)/2.5},e)},s.stringifyOptions=function(e){var n=[];return t.forEach(e,function(e,t){n.push(t+"="+e)}),n.join(",")},s}]).service("satellizer.utils",function(){this.camelCase=function(e){return e.replace(/([\:\-\_]+(.))/g,function(e,t,n,o){return o?n.toUpperCase():n})},this.parseQueryString=function(e){var n,o,r={};return t.forEach((e||"").split("&"),function(e){e&&(o=e.split("="),n=decodeURIComponent(o[0]),r[n]=t.isDefined(o[1])?decodeURIComponent(o[1]):!0)}),r}}).config(["$httpProvider","satellizer.config",function(e,t){e.interceptors.push(["$q",function(e){var n=t.tokenPrefix?t.tokenPrefix+"_"+t.tokenName:t.tokenName;return{request:function(e){var o=localStorage.getItem(n);return o&&t.httpInterceptor&&(o="Authorization"===t.authHeader?"Bearer "+o:o,e.headers[t.authHeader]=o),e},responseError:function(t){return e.reject(t)}}}])}])}(window,window.angular),function(){function e(e){this.message=e}var t="undefined"!=typeof exports?exports:this,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";e.prototype=new Error,e.prototype.name="InvalidCharacterError",t.btoa||(t.btoa=function(t){for(var o,r,i=String(t),a=0,u=n,l="";i.charAt(0|a)||(u="=",a%1);l+=u.charAt(63&o>>8-a%1*8)){if(r=i.charCodeAt(a+=.75),r>255)throw new e("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|r}return l}),t.atob||(t.atob=function(t){var o=String(t).replace(/=+$/,"");if(o.length%4==1)throw new e("'atob' failed: The string to be decoded is not correctly encoded.");for(var r,i,a=0,u=0,l="";i=o.charAt(u++);~i&&(r=a%4?64*r+i:i,a++%4)?l+=String.fromCharCode(255&r>>(-2*a&6)):0)i=n.indexOf(i);return l})}(); \ No newline at end of file diff --git a/test/auth.spec.js b/test/auth.spec.js index 7b000927..91355aad 100644 --- a/test/auth.spec.js +++ b/test/auth.spec.js @@ -137,6 +137,29 @@ describe('$auth', function() { // expect(angular.isFunction($auth.login)).toBe(true); }); + it('should be able to call loign with a redirect parameter', function() { + var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7Il9pZCI6IjUzZjYxZTEwNmZjNjFhNmMxM2I1Mjc4ZCIsImVtYWlsIjoic2FoYXQ_QG1lLmNvbSIsIl9fdiI6MH0sImlhdCI6MTQwODgyMTA5MTY3NiwiZXhwIjoxNDA5NDI1ODkxNjc2fQ.0l-ql-ZVjHiILMcMegNb3bNqapt3TZwjHy_ieduioiQ'; + var user = { + email: 'sahat?@me.com', + password: '1234' + }; + var redirect = '/new/path'; + this.config.tokenRoot = 'tokenRoot'; + this.config.loginUrl = '/auth/login'; + var response = { + tokenRoot: { + token: token + } + }; + + this.$httpBackend.expectPOST('/auth/login').respond(response); + + this.$auth.login(user, redirect); + + this.$httpBackend.flush(); + + expect(this.$location.path()).toBe(redirect); + }); }); describe('signup()', function() { diff --git a/test/local.spec.js b/test/local.spec.js index 444b85f2..f59181a4 100644 --- a/test/local.spec.js +++ b/test/local.spec.js @@ -41,6 +41,30 @@ describe('satellizer.local', function() { // }); // }); + it('should redirect to the redirect parameter on successful login', function() { + var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjp7Il9pZCI6IjUzZjYxZTEwNmZjNjFhNmMxM2I1Mjc4ZCIsImVtYWlsIjoic2FoYXQ_QG1lLmNvbSIsIl9fdiI6MH0sImlhdCI6MTQwODgyMTA5MTY3NiwiZXhwIjoxNDA5NDI1ODkxNjc2fQ.0l-ql-ZVjHiILMcMegNb3bNqapt3TZwjHy_ieduioiQ'; + var user = { + email: 'sahat?@me.com', + password: '1234' + }; + var redirect = '/new/path'; + this.config.tokenRoot = 'tokenRoot'; + this.config.loginUrl = '/auth/login'; + var response = { + tokenRoot: { + access_token: token + } + }; + + this.$httpBackend.expectPOST('/auth/login').respond(response); + + this.local.login(user, redirect); + + this.$httpBackend.flush(); + + expect(this.$location.path()).toBe(redirect); + }); + it('should fail login with incorrect credentials', function() { var result = null; var user = { diff --git a/test/shared.spec.js b/test/shared.spec.js index 0590e8f5..8b5108ca 100644 --- a/test/shared.spec.js +++ b/test/shared.spec.js @@ -106,6 +106,66 @@ describe('satellizer.shared', function() { expect(token).toEqual(this.shared.getToken()); }); + it('should redirect when the redirect parameter is set', function() { + this.config.tokenRoot = 'tokenRoot' + var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJsb…YzMn0.YATZN37JENCQWeNAoN4M7KxJl7OAIJL4ka_fSM_gYkE' + + var response = { + data: { + tokenRoot: { + access_token: token + } + } + }; + + var redirect = "/new/path"; + + this.shared.setToken(response, redirect); + + expect(this.$location.path()).toEqual(redirect); + }); + + it('should redirect when the loginRedirect config property is set', function() { + var configRedirect = "/my-login-url"; + this.config.loginRedirect = configRedirect; + this.config.tokenRoot = 'tokenRoot'; + var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJsb…YzMn0.YATZN37JENCQWeNAoN4M7KxJl7OAIJL4ka_fSM_gYkE' + + var response = { + data: { + tokenRoot: { + access_token: token + } + } + }; + + this.shared.setToken(response); + + expect(this.$location.path()).toEqual(configRedirect); + }); + + + it('should redirect to the redirect parameter even if the loginRedirect config property is set', function() { + var configRedirect = "/my-login-url"; + this.config.loginRedirect = configRedirect; + this.config.tokenRoot = 'tokenRoot'; + var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJsb…YzMn0.YATZN37JENCQWeNAoN4M7KxJl7OAIJL4ka_fSM_gYkE' + + var response = { + data: { + tokenRoot: { + access_token: token + } + } + }; + + var redirect = "/new/path"; + + this.shared.setToken(response, redirect); + + expect(this.$location.path()).toEqual(redirect); + }); + }); describe('removeToken()', function() {