-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #109 from FusionAuth/108-angular-ssr-support
[Angular] SSR support
- Loading branch information
Showing
9 changed files
with
148 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
{ | ||
"root": false, | ||
"extends": "../../.eslintrc" | ||
"extends": "../../.eslintrc", | ||
"rules": { | ||
"@typescript-eslint/ban-types": "off" | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/SSRCookieAdapter.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// import { CookieAdapter } from '@fusionauth-sdk/core'; | ||
|
||
/** An adapter class that supports accessing cookies with SSR */ | ||
export class SSRCookieAdapter /* implements CookieAdapter */ { | ||
constructor(private isBrowser: boolean) {} | ||
|
||
at_exp(cookieName: string = 'app.at_exp') { | ||
if (!this.isBrowser) { | ||
return; | ||
} | ||
|
||
try { | ||
const expCookie = document.cookie | ||
.split('; ') | ||
.map(c => c.split('=')) | ||
.find(([name]) => name === cookieName); | ||
return expCookie?.[1]; | ||
} catch (error) { | ||
console.error('Error within the SSRCookieAdapter: ', error); | ||
return -1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,40 @@ | ||
import { TestBed } from '@angular/core/testing'; | ||
import { fakeAsync, flush, tick } from '@angular/core/testing'; | ||
import { take } from 'rxjs'; | ||
|
||
import { FusionAuthConfig } from './types'; | ||
import { FusionAuthService } from './fusion-auth.service'; | ||
import { fakeAsync, flush, tick } from '@angular/core/testing'; | ||
import { FusionAuthModule } from './fusion-auth.module'; | ||
import { mockIsLoggedIn, removeAt_expCookie } from './core'; | ||
import { take } from 'rxjs'; | ||
|
||
const config: FusionAuthConfig = { | ||
clientId: 'a-client-id', | ||
redirectUri: 'http://my-app.com', | ||
serverUrl: 'http://localhost:9011', | ||
}; | ||
|
||
function configureTestingModule(config: FusionAuthConfig) { | ||
TestBed.configureTestingModule({ | ||
imports: [FusionAuthModule.forRoot(config)], | ||
}); | ||
return TestBed.inject(FusionAuthService); | ||
} | ||
|
||
describe('FusionAuthService', () => { | ||
afterEach(() => { | ||
removeAt_expCookie(); | ||
localStorage.clear(); | ||
}); | ||
|
||
const config: FusionAuthConfig = { | ||
clientId: 'a-client-id', | ||
redirectUri: 'http://my-app.com', | ||
serverUrl: 'http://localhost:9011', | ||
}; | ||
|
||
it('Can be configured to automatically handle getting userInfo', (done: DoneFn) => { | ||
mockIsLoggedIn(); | ||
|
||
const user = { email: '[email protected]' }; | ||
spyOn(window, 'fetch').and.resolveTo( | ||
new Response( | ||
JSON.stringify({ | ||
email: '[email protected]', | ||
}), | ||
{ status: 200 }, | ||
), | ||
new Response(JSON.stringify(user), { status: 200 }), | ||
); | ||
|
||
const service = new FusionAuthService(config); | ||
const service = configureTestingModule(config); | ||
|
||
service.getUserInfoObservable().subscribe({ | ||
next: userInfo => { | ||
|
@@ -46,7 +52,7 @@ describe('FusionAuthService', () => { | |
new Response(null, { status: responseStatus }), | ||
); | ||
|
||
const service = new FusionAuthService(config); | ||
const service = configureTestingModule(config); | ||
|
||
service.getUserInfoObservable().subscribe({ | ||
error: error => { | ||
|
@@ -61,7 +67,7 @@ describe('FusionAuthService', () => { | |
it("Contains an observable 'isLoggedin$' property that becomes false as the access token expires.", fakeAsync(() => { | ||
mockIsLoggedIn(); // sets `app.at_exp` cookie so user is logged in for 1 hour. | ||
|
||
const service = new FusionAuthService(config); | ||
const service = configureTestingModule(config); | ||
|
||
tick(60 * 59 * 1000); | ||
service.isLoggedIn$.pipe(take(1)).subscribe(isLoggedIn => { | ||
|
@@ -80,7 +86,7 @@ describe('FusionAuthService', () => { | |
mockIsLoggedIn(); | ||
const spy = spyOn(FusionAuthService.prototype, 'initAutoRefresh'); | ||
|
||
const service = new FusionAuthService({ | ||
const service = configureTestingModule({ | ||
...config, | ||
shouldAutoRefresh: true, | ||
}); | ||
|
@@ -90,14 +96,18 @@ describe('FusionAuthService', () => { | |
}); | ||
|
||
it("Does not invoke 'initAutoRefresh' if the user is not logged in", () => { | ||
const spy = spyOn(FusionAuthService.prototype, 'initAutoRefresh'); | ||
const service = new FusionAuthService({ | ||
const initAutoRefreshSpy = spyOn( | ||
FusionAuthService.prototype, | ||
'initAutoRefresh', | ||
); | ||
|
||
const service = configureTestingModule({ | ||
...config, | ||
shouldAutoRefresh: true, | ||
}); | ||
|
||
expect(service.isLoggedIn()).toBe(false); | ||
expect(spy).not.toHaveBeenCalled(); | ||
expect(initAutoRefreshSpy).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("Invokes an 'onRedirect' callback", () => { | ||
|
@@ -107,8 +117,7 @@ describe('FusionAuthService', () => { | |
localStorage.setItem('fa-sdk-redirect-value', `abc123:${stateValue}`); | ||
|
||
const onRedirect = jasmine.createSpy('onRedirectSpy'); | ||
|
||
new FusionAuthService({ ...config, onRedirect }); | ||
configureTestingModule({ ...config, onRedirect }); | ||
|
||
expect(onRedirect).toHaveBeenCalledWith('/welcome-page'); | ||
}); | ||
|
@@ -118,7 +127,7 @@ describe('FusionAuthService', () => { | |
spyOn(window, 'fetch').and.resolveTo(new Response(null, { status: 400 })); | ||
const onAutoRefreshFailure = jasmine.createSpy('onAutoRefreshFailureSpy'); | ||
|
||
new FusionAuthService({ | ||
configureTestingModule({ | ||
...config, | ||
shouldAutoRefresh: true, | ||
onAutoRefreshFailure, | ||
|
Oops, something went wrong.