【问题标题】:angular-auth-oidc-client in a Ionic Android applicationIonic Android 应用程序中的 angular-auth-oidc-client
【发布时间】:2021-10-28 21:24:06
【问题描述】:

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

版本:

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

电容平台:Android

我在哪里:

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

接下来要做什么?身份验证仍然是错误的。我应该用回调 queryString 调用什么?

我发现这个 CallBackService 似乎符合我的需要,但不幸的是它不是 lib 公共 API 的一部分:/

【问题讨论】:

    标签: angular ionic-framework ionic-native angular-auth-oidc-client


    【解决方案1】:

    请注意,此解决方案仅适用于刷新令牌(在 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 服务将 Keycloak 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(注意 eagerLoadAuthWellKnownEndpoints 和 useRefreshToken):

    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',
      },
    };
    

    【讨论】:

      猜你喜欢
      • 2021-09-17
      • 2021-01-22
      • 2016-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      • 2019-11-23
      • 1970-01-01
      相关资源
      最近更新 更多