From 5b7c73bc76d0b759b71431b155a44003b9733515 Mon Sep 17 00:00:00 2001 From: Louis Zuckerman Date: Wed, 26 Jul 2017 16:33:54 -0400 Subject: [PATCH 1/4] spira/angular-jwt-auth#66 add tests to fail removing cookie with domain --- src/fixtures.spec.ts | 11 +++++++++-- src/service/ngJwtAuthService.spec.ts | 13 ++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/fixtures.spec.ts b/src/fixtures.spec.ts index 0083bda..8966950 100644 --- a/src/fixtures.spec.ts +++ b/src/fixtures.spec.ts @@ -103,8 +103,15 @@ export function cookiesFactoryMock(allowDomain:string) { return cookieStore[key]; }, - remove: (key) => { - delete cookieStore[key]; + remove: (key, conf) => { + let cookie = cookieStore[key]; + if (cookie && cookie.conf && cookie.conf.domain) { + if (conf && conf.domain && conf.domain === cookie.conf.domain) { + delete cookieStore[key]; + } + } else { + delete cookieStore[key]; + } } }; }; diff --git a/src/service/ngJwtAuthService.spec.ts b/src/service/ngJwtAuthService.spec.ts index a6404d3..4d30449 100644 --- a/src/service/ngJwtAuthService.spec.ts +++ b/src/service/ngJwtAuthService.spec.ts @@ -856,7 +856,7 @@ describe('Service tests', () => { }); - it('should be able to configure the cookie to be saved to the top level domain', () => { + it('should be able to configure the cookie to be saved to the top level domain and remove when logging out', () => { expect(config.cookie.enabled).to.be.true; //check the service is configured to save cookies @@ -872,12 +872,19 @@ describe('Service tests', () => { $httpBackend.flush(); - let cookie = $cookies.get(config.cookie.name); + let cookieExists = $cookies.get(config.cookie.name); let cookieObject = $cookies.getObject(config.cookie.name); - expect(cookie).to.equal(token); + expect(cookieExists).to.equal(token); expect(cookieObject.conf.domain).to.equal(cookieDomain); + expect(cookieObject.conf.expires).to.be.instanceOf(Date); + + ngJwtAuthService.logout(); //logout + + let cookieMissing = $cookies.get(config.cookie.name); + + expect(cookieMissing).to.be.undefined; }); From 67ad2ff79ca31017019661cfb5932a491a0819d8 Mon Sep 17 00:00:00 2001 From: Louis Zuckerman Date: Wed, 26 Jul 2017 16:36:04 -0400 Subject: [PATCH 2/4] spira/angular-jwt-auth#66 add code to properly remove cookie with domain, making tests pass --- src/service/ngJwtAuthService.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/service/ngJwtAuthService.ts b/src/service/ngJwtAuthService.ts index c21496c..d6f4c86 100644 --- a/src/service/ngJwtAuthService.ts +++ b/src/service/ngJwtAuthService.ts @@ -29,6 +29,8 @@ export class NgJwtAuthService { private refreshTimerPromise:ng.IPromise; private tokenData:IJwtToken; + private topLevelDomainName:string; + //public properties public user:IUser; public loggedIn:boolean = false; @@ -409,8 +411,11 @@ export class NgJwtAuthService { this.$window.localStorage.removeItem(this.config.storageKeyName); if (this.config.cookie.enabled) { - - this.$cookies.remove(this.config.cookie.name); + let options = undefined; + if (this.topLevelDomainName) { + options = {domain: this.topLevelDomainName} + } + this.$cookies.remove(this.config.cookie.name, options); } this.unsetJWTHeader(); @@ -588,6 +593,9 @@ export class NgJwtAuthService { }); if (this.$cookies.get(cookieKey)) { //saving the cookie worked, it must be the top level domain + if (testHostname && !this.topLevelDomainName) { + this.topLevelDomainName = testHostname + } return; //so exit here } From 4968f7860335b3990191114e00949f960a13e168 Mon Sep 17 00:00:00 2001 From: Louis Zuckerman Date: Fri, 28 Jul 2017 19:02:17 -0400 Subject: [PATCH 3/4] spira/angular-jwt-auth#66 fix saving new cookie after token refresh update readme --- README.md | 19 +++++++++++++++++++ src/ngJwtAuthInterfaces.ts | 1 + src/provider/ngJwtAuthServiceProvider.ts | 1 + src/service/ngJwtAuthService.ts | 18 +++++++++--------- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f4f4838..102f0f2 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,25 @@ namespace app.guest.login { } ``` +## Cookies + +Cookies are helpful for adding the authentication token to external, non angular, requests to an API. For example, if +you have an `` tag that references a dynamic image on the API that requires authentication. The browser can add +the token in a cookie when making the image request. + +To enable cookies set `config.cookie.enabled = true`. + +You can optionally allow the cookie to be used for requests on other subdomains. This is useful if your webapp is on +one subdomain but your API is on a different subdomain. + +angular-jwt-auth can automatically find the top level domain (like `example.com`) or you can provide a specific domain +name. Note that your domain must be the top level domain (or a subdomain) of the location in the browser URL. + +To enable automatic top level domain detection set `config.cookie.topLevelDomain = true`. + +To set a specific top level domain name set `config.cookie.topLevelDomainName = "your.domain.name"` + + ## Todo * Better documentation with examples in typescript. * Site hosted on github showing off examples with material \ No newline at end of file diff --git a/src/ngJwtAuthInterfaces.ts b/src/ngJwtAuthInterfaces.ts index b7ca940..16dbd42 100644 --- a/src/ngJwtAuthInterfaces.ts +++ b/src/ngJwtAuthInterfaces.ts @@ -10,6 +10,7 @@ export interface ICookieConfig { enabled:boolean; name?:string; topLevelDomain?:boolean; + topLevelDomainName?:string; } export interface INgJwtAuthServiceConfig { diff --git a/src/provider/ngJwtAuthServiceProvider.ts b/src/provider/ngJwtAuthServiceProvider.ts index 58c6a00..4688fb5 100644 --- a/src/provider/ngJwtAuthServiceProvider.ts +++ b/src/provider/ngJwtAuthServiceProvider.ts @@ -56,6 +56,7 @@ export class NgJwtAuthServiceProvider implements ng.IServiceProvider { enabled: false, name: 'ngJwtAuthToken', topLevelDomain: false, + topLevelDomainName: null, } }; diff --git a/src/service/ngJwtAuthService.ts b/src/service/ngJwtAuthService.ts index d6f4c86..9f8a3cb 100644 --- a/src/service/ngJwtAuthService.ts +++ b/src/service/ngJwtAuthService.ts @@ -29,8 +29,6 @@ export class NgJwtAuthService { private refreshTimerPromise:ng.IPromise; private tokenData:IJwtToken; - private topLevelDomainName:string; - //public properties public user:IUser; public loggedIn:boolean = false; @@ -412,8 +410,8 @@ export class NgJwtAuthService { if (this.config.cookie.enabled) { let options = undefined; - if (this.topLevelDomainName) { - options = {domain: this.topLevelDomainName} + if (this.config.cookie.topLevelDomainName) { + options = {domain: this.config.cookie.topLevelDomainName} } this.$cookies.remove(this.config.cookie.name, options); } @@ -578,8 +576,12 @@ export class NgJwtAuthService { let cookieKey = this.config.cookie.name, expires = new Date(tokenData.data.exp * 1000); //set the cookie expiry to the same as the jwt - if (this.config.cookie.topLevelDomain) { - + if (this.config.cookie.topLevelDomainName) { + this.$cookies.put(cookieKey, rawToken, { + domain: this.config.cookie.topLevelDomainName, + expires: expires, + }); + } else if (this.config.cookie.topLevelDomain) { let hostnameParts = this.$location.host().split('.'); let segmentCount = 1; let testHostname = ''; @@ -593,9 +595,7 @@ export class NgJwtAuthService { }); if (this.$cookies.get(cookieKey)) { //saving the cookie worked, it must be the top level domain - if (testHostname && !this.topLevelDomainName) { - this.topLevelDomainName = testHostname - } + this.config.cookie.topLevelDomainName = testHostname; return; //so exit here } From 5186a61b2212e4ee11a4e96f71083c10f58fcace Mon Sep 17 00:00:00 2001 From: Louis Zuckerman Date: Fri, 28 Jul 2017 19:36:22 -0400 Subject: [PATCH 4/4] spira/angular-jwt-auth#66 refresh token if cookie is enabled but missing --- src/service/ngJwtAuthService.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/service/ngJwtAuthService.ts b/src/service/ngJwtAuthService.ts index 9f8a3cb..3338c35 100644 --- a/src/service/ngJwtAuthService.ts +++ b/src/service/ngJwtAuthService.ts @@ -135,7 +135,15 @@ export class NgJwtAuthService { ; //needs to refresh if the the next time we could refresh is after the configured refresh before date - return (latestRefresh <= nextRefreshOpportunity); + return (latestRefresh <= nextRefreshOpportunity || this.cookieIsMissing()); + } + + /** + * Check if there should be a cookie, but it is missing + * @returns {boolean} + */ + private cookieIsMissing():boolean { + return this.config.cookie.enabled && !this.$cookies.get(this.config.cookie.name); } /**