-
Notifications
You must be signed in to change notification settings - Fork 434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fabiangosebrink/create popup example with better API #960
Merged
Merged
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
208d7c5
adding application
FabianGosebrink d2eee65
adding popup
FabianGosebrink 26f25c1
last popup steps
FabianGosebrink e3f2860
formatting
FabianGosebrink 6392f33
using default cert and standard cert
damienbod b83838b
first working draft
FabianGosebrink aca623e
solved the issues
FabianGosebrink d30c5cf
added new returntype
FabianGosebrink 87be9a8
fixed tests
FabianGosebrink af21b2a
tested login service
FabianGosebrink b843276
added more unit tests
FabianGosebrink 3fac34b
added more tests
FabianGosebrink 2701e2f
docs
FabianGosebrink 1489874
fixing the warning
FabianGosebrink cad55b1
switch to port 4205 for testing
damienbod ce18db8
update popup UI
damienbod 58ec3d6
added more tests
FabianGosebrink 8646e44
pushed coverage
FabianGosebrink ba02895
updated the docs
FabianGosebrink 519652f
fixed prettier
FabianGosebrink File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,14 +1,30 @@ | ||
# Authorizing in a popup or iframe | ||
# Authorizing in a popup | ||
|
||
You can call the Provider's authorization endpoint in a popup or iframe instead of navigating to it in the app's parent window. | ||
This allows you to have the Provider's consent prompt display in a popup window to avoid unloading and reloading the app, | ||
or to authorize the user silently by loading the endpoint in a hidden iframe if that supported by the Provider. | ||
You can call the Provider's authorization endpoint in a popup instead of navigating to it in the app's parent window. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can authenticate tith any Open ID Connect identity provider using a popup. |
||
This allows you to have the provider's consent prompt display in a popup window to avoid unloading and reloading the app. | ||
|
||
To get the fully-formed authorization URL, pass a handler function to `OidcSecurityService.authorize` | ||
(this will also prevent the default behavior of loading the authorization endpoint in the current window): | ||
Sample: | ||
|
||
```typescript | ||
login() { | ||
this.oidcSecurityService.authorizeWithPopUp(); | ||
} | ||
userData$: Observable<any>; | ||
|
||
isAuthenticated: boolean; | ||
|
||
constructor(public oidcSecurityService: OidcSecurityService) {} | ||
|
||
ngOnInit() { | ||
this.oidcSecurityService.checkAuth().subscribe((isAuthenticated) => { | ||
console.log('app authenticated', isAuthenticated); | ||
const at = this.oidcSecurityService.getToken(); | ||
console.log(`Current access token is '${at}'`); | ||
}); | ||
} | ||
|
||
loginWithPopup() { | ||
this.oidcSecurityService.authorizeWithPopUp().subscribe(({ isAuthenticated, userData, accessToken }) => { | ||
console.log(isAuthenticated); | ||
console.log(userData); | ||
console.log(accessToken); | ||
}); | ||
} | ||
``` |
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
13 changes: 13 additions & 0 deletions
13
projects/angular-auth-oidc-client/src/lib/check-auth.service-mock.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,13 @@ | ||
import { Injectable } from '@angular/core'; | ||
import { Observable, of } from 'rxjs'; | ||
|
||
@Injectable() | ||
export class CheckAuthServiceMock { | ||
checkAuth(url?: string): Observable<boolean> { | ||
return of(null); | ||
} | ||
|
||
checkAuthIncludingServer(): Observable<boolean> { | ||
return of(null); | ||
} | ||
} |
255 changes: 255 additions & 0 deletions
255
projects/angular-auth-oidc-client/src/lib/check-auth.service.spec.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,255 @@ | ||
import { HttpClientModule } from '@angular/common/http'; | ||
import { TestBed, waitForAsync } from '@angular/core/testing'; | ||
import { BrowserModule } from '@angular/platform-browser'; | ||
import { RouterTestingModule } from '@angular/router/testing'; | ||
import { of, throwError } from 'rxjs'; | ||
import { AuthModule } from './auth.module'; | ||
import { AuthStateService } from './authState/auth-state.service'; | ||
import { CallbackService } from './callback/callback.service'; | ||
import { PeriodicallyTokenCheckService } from './callback/periodically-token-check.service'; | ||
import { RefreshSessionService } from './callback/refresh-session.service'; | ||
import { ConfigurationProvider } from './config/config.provider'; | ||
import { CheckSessionService } from './iframe/check-session.service'; | ||
import { SilentRenewService } from './iframe/silent-renew.service'; | ||
import { LoggerService } from './logging/logger.service'; | ||
import { LoggerServiceMock } from './logging/logger.service-mock'; | ||
import { PopUpService } from './login/popup.service'; | ||
import { OidcSecurityService } from './oidc.security.service'; | ||
import { UserService } from './userData/user-service'; | ||
|
||
describe('CheckAuthService', () => { | ||
let oidcSecurityService: OidcSecurityService; | ||
let configurationProvider: ConfigurationProvider; | ||
let authStateService: AuthStateService; | ||
let userService: UserService; | ||
let checkSessionService: CheckSessionService; | ||
let callBackService: CallbackService; | ||
let silentRenewService: SilentRenewService; | ||
let periodicallyTokenCheckService: PeriodicallyTokenCheckService; | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [BrowserModule, HttpClientModule, RouterTestingModule, AuthModule.forRoot()], | ||
providers: [ | ||
CheckSessionService, | ||
SilentRenewService, | ||
UserService, | ||
{ provide: LoggerService, useClass: LoggerServiceMock }, | ||
ConfigurationProvider, | ||
AuthStateService, | ||
CallbackService, | ||
RefreshSessionService, | ||
PeriodicallyTokenCheckService, | ||
PopUpService, | ||
], | ||
}); | ||
}); | ||
|
||
beforeEach(() => { | ||
oidcSecurityService = TestBed.inject(OidcSecurityService); | ||
configurationProvider = TestBed.inject(ConfigurationProvider); | ||
userService = TestBed.inject(UserService); | ||
authStateService = TestBed.inject(AuthStateService); | ||
checkSessionService = TestBed.inject(CheckSessionService); | ||
callBackService = TestBed.inject(CallbackService); | ||
silentRenewService = TestBed.inject(SilentRenewService); | ||
|
||
periodicallyTokenCheckService = TestBed.inject(PeriodicallyTokenCheckService); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(oidcSecurityService).toBeTruthy(); | ||
}); | ||
|
||
describe('checkAuth', () => { | ||
it( | ||
'returns false when config is not valid', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(false); | ||
oidcSecurityService.checkAuth().subscribe((result) => expect(result).toBeFalse()); | ||
}) | ||
); | ||
|
||
it( | ||
'returns false in case handleCallbackAndFireEvents throws an error', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'isCallback').and.returnValue(true); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
const spy = spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(throwError('ERROR')); | ||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(result).toBeFalse(); | ||
expect(spy).toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'calls callbackService.handlePossibleStsCallback with current url when callback is true', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'isCallback').and.returnValue(true); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
const spy = spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(result).toBeTrue(); | ||
expect(spy).toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'does NOT call handleCallbackAndFireEvents with current url when callback is false', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'isCallback').and.returnValue(false); | ||
const spy = spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(result).toBeFalse(); | ||
expect(spy).not.toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'does fire the auth and user data events when it is not a callback from the sts and is authenticated', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'isCallback').and.returnValue(false); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
|
||
const setAuthorizedAndFireEventSpy = spyOn(authStateService, 'setAuthorizedAndFireEvent'); | ||
const userServiceSpy = spyOn(userService, 'publishUserDataIfExists'); | ||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(result).toBeTrue(); | ||
expect(setAuthorizedAndFireEventSpy).toHaveBeenCalled(); | ||
expect(userServiceSpy).toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'does NOT fire the auth and user data events when it is not a callback from the sts and is NOT authenticated', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'isCallback').and.returnValue(false); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(false); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
|
||
const setAuthorizedAndFireEventSpy = spyOn(authStateService, 'setAuthorizedAndFireEvent'); | ||
const userServiceSpy = spyOn(userService, 'publishUserDataIfExists'); | ||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(result).toBeFalse(); | ||
expect(setAuthorizedAndFireEventSpy).not.toHaveBeenCalled(); | ||
expect(userServiceSpy).not.toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'if authenticated return true', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
|
||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(result).toBeTrue(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'if authenticated set auth and fires event ', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
|
||
const spy = spyOn(authStateService, 'setAuthorizedAndFireEvent'); | ||
|
||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(spy).toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'if authenticated publishUserdataIfExists ', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
|
||
const spy = spyOn(userService, 'publishUserDataIfExists'); | ||
|
||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(spy).toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'if authenticated callbackService startTokenValidationPeriodically', | ||
waitForAsync(() => { | ||
const config = { | ||
stsServer: 'stsServer', | ||
tokenRefreshInSeconds: 7, | ||
}; | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue(config); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
|
||
const spy = spyOn(periodicallyTokenCheckService, 'startTokenValidationPeriodically'); | ||
|
||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(spy).toHaveBeenCalledWith(7); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'if isCheckSessionConfigured call checkSessionService.start()', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
|
||
spyOn(checkSessionService, 'isCheckSessionConfigured').and.returnValue(true); | ||
const spy = spyOn(checkSessionService, 'start'); | ||
|
||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(spy).toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
|
||
it( | ||
'if isSilentRenewConfigured call getOrCreateIframe()', | ||
waitForAsync(() => { | ||
spyOn(configurationProvider, 'hasValidConfig').and.returnValue(true); | ||
spyOnProperty(configurationProvider, 'openIDConfiguration', 'get').and.returnValue('stsServer'); | ||
spyOn(callBackService, 'handleCallbackAndFireEvents').and.returnValue(of(null)); | ||
spyOn(authStateService, 'areAuthStorageTokensValid').and.returnValue(true); | ||
|
||
spyOn(silentRenewService, 'isSilentRenewConfigured').and.returnValue(true); | ||
const spy = spyOn(silentRenewService, 'getOrCreateIframe'); | ||
|
||
oidcSecurityService.checkAuth().subscribe((result) => { | ||
expect(spy).toHaveBeenCalled(); | ||
}); | ||
}) | ||
); | ||
}); | ||
}); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Authentication using a Popup