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/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/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.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; }); diff --git a/src/service/ngJwtAuthService.ts b/src/service/ngJwtAuthService.ts index c21496c..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); } /** @@ -409,8 +417,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.config.cookie.topLevelDomainName) { + options = {domain: this.config.cookie.topLevelDomainName} + } + this.$cookies.remove(this.config.cookie.name, options); } this.unsetJWTHeader(); @@ -573,8 +584,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 = ''; @@ -588,6 +603,7 @@ export class NgJwtAuthService { }); if (this.$cookies.get(cookieKey)) { //saving the cookie worked, it must be the top level domain + this.config.cookie.topLevelDomainName = testHostname; return; //so exit here }