Ionic Android应用程序中的angular auth-oidc客户端



我正试图在安卓Ionic Angular应用程序中使用angular-auth-oidc-client对MS Identity服务器进行身份验证。

版本:

  • angular-auth-oidc-client11.1.4
  • @angular10.0.2
  • @ionic/angular5.2.3

电容器平台:安卓

我所在的位置:

  • 运行纯web应用程序时(从桌面浏览器(验证成功
  • 在android清单中声明了一个意向过滤器,当授权服务器重定向到我的-app://login-callback(真正的Android设备(
  • 使用Deeplinks插件,我可以拦截对登录回调的调用,并可以读取包含代码、范围、状态和session_state参数的查询字符串

下一步该怎么办?身份验证仍然为假。我应该用回调queryString调用什么?

我找到了这个CallBackService,它似乎符合我的需求,但不幸的是,它不是lib公共API的一部分:/

请注意,此解决方案仅适用于刷新令牌(在conf中设置useRefreshToken: true(。我无法使用silentRenewUrl使其正常工作(还没有?(

首先,AppComponent:

export class AppComponent implements OnInit, OnDestroy {
currentUser: KeycloakUser;
private deeplinksRouteSubscription: Subscription;
constructor(
private deeplinks: Deeplinks,
private navController: NavController,
private platform: Platform,
private uaa: UaaService,
private changedetector: ChangeDetectorRef
) {}
async ngOnInit() {
await this.platform.ready();
console.log('PLATFORMS: ' + this.platform.platforms());
if (this.platform.is('capacitor')) {
this.setupDeeplinks();
const { SplashScreen, StatusBar } = Plugins;
StatusBar.setStyle({ style: StatusBarStyle.Light });
SplashScreen.hide();
}
await this.initUaa();
}
ngOnDestroy() {
this.deeplinksRouteSubscription.unsubscribe();
}
login() {
this.uaa.login();
}
logout() {
this.uaa.logout();
}
private setupDeeplinks() {
this.deeplinks.routeWithNavController(this.navController, {}).subscribe(
(match) =>
this.navController
.navigateForward(match.$link.path + '?' + match.$link.queryString)
.then(async () => await this.initUaa()),
(nomatch) =>
console.error(
"Got a deeplink that didn't match",
JSON.stringify(nomatch)
)
);
}
private async initUaa(): Promise<void> {
await this.uaa.init();
this.uaa.currentUser$.subscribe((u) => {
if (this.currentUser !== u) {
this.currentUser = u;
this.changedetector.detectChanges();
}
});
}
}

现在,我使用的UAA服务将Key斗篷ID令牌转换为用户对象。实际初始化发生在onBackOnline():中

import { Injectable, OnDestroy } from '@angular/core';
import { OidcSecurityService } from 'angular-auth-oidc-client';
import {
BehaviorSubject,
fromEvent,
merge,
Observable,
Subscription,
} from 'rxjs';
import { map } from 'rxjs/operators';
import { KeycloakUser } from './domain/keycloak-user';
@Injectable({ providedIn: 'root' })
export class UaaService implements OnDestroy {
private user$ = new BehaviorSubject<KeycloakUser>(KeycloakUser.ANONYMOUS);
private userdataSubscription: Subscription;
constructor(private oidcSecurityService: OidcSecurityService) {
console.log(
`Starting UaaService in ${navigator.onLine ? 'online' : 'offline'} mode`
);
merge<boolean>(
fromEvent(window, 'offline').pipe(
map((): boolean => {
console.log('Switching UaaService to offline mode');
return true;
})
),
fromEvent(window, 'online').pipe(
map((): boolean => {
console.log('Switching UaaService to online mode');
return false;
})
)
).subscribe((isOffline: boolean) => {
if (isOffline) {
this.onOffline();
} else {
this.onBackOnline();
}
});
}
public ngOnDestroy() {
this.userdataSubscription.unsubscribe();
}
public async init(): Promise<boolean> {
if (!navigator.onLine) {
this.user$.next(KeycloakUser.ANONYMOUS);
return false;
}
const user = await this.onBackOnline();
return !!user.sub;
}
private async onBackOnline(): Promise<KeycloakUser> {
const isAlreadyAuthenticated = await this.oidcSecurityService
.checkAuth()
.toPromise()
.catch(() => false);
const user = UaaService.fromToken(
this.oidcSecurityService.getPayloadFromIdToken()
);
console.log('UaaService::onBackOnline', isAlreadyAuthenticated, user);
this.userdataSubscription?.unsubscribe();
this.userdataSubscription = this.oidcSecurityService.isAuthenticated$.subscribe(
() =>
this.user$.next(
UaaService.fromToken(this.oidcSecurityService.getPayloadFromIdToken())
)
);
return user;
}
private static fromToken = (idToken: any) =>
idToken?.sub
? new KeycloakUser({
sub: idToken.sub,
preferredUsername: idToken.preferred_username,
roles: idToken?.resource_access?.['tahiti-devops']?.roles || [],
})
: KeycloakUser.ANONYMOUS;
private onOffline() {
this.userdataSubscription?.unsubscribe();
}
get currentUser$(): Observable<KeycloakUser> {
return this.user$;
}
public login(): void {
this.oidcSecurityService.authorize();
}
public logout(): boolean {
this.oidcSecurityService.logoff();
if (this.user$.value !== KeycloakUser.ANONYMOUS) {
this.user$.next(KeycloakUser.ANONYMOUS);
return true;
}
return false;
}
}

这是我使用的conf(注意eagerLoadAuthWellKnownEndpointsuseRefreshToken(:

import { LogLevel } from 'angular-auth-oidc-client';
export const environment = {
production: false,
openIdConfiguration: {
// https://github.com/damienbod/angular-auth-oidc-client/blob/master/docs/configuration.md
clientId: 'tahiti-devops',
forbiddenRoute: '/settings',
eagerLoadAuthWellKnownEndpoints: false,
ignoreNonceAfterRefresh: true, // Keycloak sends refresh_token with nonce
logLevel: LogLevel.Warn,
postLogoutRedirectUri: 'com.c4-soft://device/cafe-skifo',
redirectUrl: 'com.c4-soft://device/cafe-skifo',
renewTimeBeforeTokenExpiresInSeconds: 10,
responseType: 'code',
scope: 'email openid offline_access roles',
silentRenew: true,
// silentRenewUrl: 'com.c4soft.mobileapp://cafe-skifo/silent-renew-pkce.html',
useRefreshToken: true,
stsServer: 'https://laptop-jerem:8443/auth/realms/master',
unauthorizedRoute: '/settings',
},
};

最新更新