Ionic 离子和MSAL身份验证

b1uwtaje  于 2022-12-16  发布在  Ionic
关注(0)|答案(3)|浏览(129)

我有一个需要在Azure中进行身份验证的Ionic应用,因此我按照本教程安装了MSAL:https://learn.microsoft.com/en-us/graph/tutorials/angular
它的工作原理就像一个魅力与“离子服务”,但当我运行它在设备中,它崩溃时,我试图登录Azure。我认为这是因为弹出窗口MSAL显示登录是不允许在离子。
所以我的第一个尝试是将loginPopup()调用改为loginRedirect(),所以我删除了以下代码:

async signIn(): Promise<void> {
  const result = await this.msalService
    .loginPopup(OAuthSettings)
    .toPromise()
    .catch((reason) => {
      this.alertsService.addError('Login failed',
        JSON.stringify(reason, null, 2));
    });

  if (result) {
    this.msalService.instance.setActiveAccount(result.account);
    this.authenticated = true;
    this.user = await this.getUser();
  }
}

我添加了这个新的(基于https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/errors.md):

async signIn(): Promise<void> {
  await this.msalService.instance.handleRedirectPromise();

  const accounts = this.msalService.instance.getAllAccounts();
  if (accounts.length === 0) {
    // No user signed in
    this.msalService.instance.loginRedirect();
  }
}

但是这种方法并没有保存用户信息,因为我没有一个“结果”来处理或调用setActiveAccount(result),即使在“ionicserve”中也不起作用,所以我放弃了这种方法。
第二种方法是在InAppBrowser(https://ionicframework.com/docs/native/in-app-browser)中显示弹出窗口,为此我花了两天时间寻找可能的解决方案,然后将代码更改为:

async signIn(): Promise<void> {
  const browser = this.iab.create('https://www.microsoft.com/');
  browser.executeScript({ code: "\
    const result = await this.msalService\
      .loginPopup(OAuthSettings)\
      .toPromise()\
      .catch((reason) => {\
        this.alertsService.addError('Login failed',\
          JSON.stringify(reason, null, 2));\
      });\
    if (result) {\
      this.msalService.instance.setActiveAccount(result.account);\
      this.authenticated = true;\
      this.user = await this.getUser();\
    }"
  }); 
}

但它只是打开一个新窗口,不执行任何操作,它不执行loginPopup(),因此我也放弃了第二种方法。
有人知道如何避免弹出问题在离子?
谢谢

sg24os4d

sg24os4d1#

我设法解决这个问题与cordova-plugin-inappbrowser通过使用自定义导航客户端,这里是我的实现:

自定义导航客户端

class CustomNavigationClient extends NavigationClient {
      async navigateExternal(url: string, options: any) {
        // Cortdova implementation
        if (window.hasOwnProperty("cordova")) {
          var ref = cordova.InAppBrowser.open(url, '_blank', 'location=yes,clearcache=yes,clearsessioncache=yes');

          // Check if the appbrowser started a navigation
          ref.addEventListener('loadstart', (event: any) => {
            // Check if the url contains the #state login parameter
            if (event.url.includes('#state')) {
              // Close the in app browser and redirect to localhost + the state parameter
              // msal-login is a fake route to trigger a page refresh
              ref.close();
              const domain = event.url.split('#')[0];
              const url = event.url.replace(domain, 'http://localhost/msal-login');
              window.location.href = url;
            }
          });
        } else {
          if (options.noHistory) {
            window.location.replace(url);
          } else {
            window.location.assign(url);
          }
        }
        return true;
      }
    }

应用程序组件.ts

const navigationClient = new CustomNavigationClient();
    this.msalService.instance.setNavigationClient(navigationClient);
    
    this.msalService.instance.handleRedirectPromise().then((authResult: any) => {
      console.debug('AuthResult ---->', authResult);
      if (authResult) { 
        // your login logic goes here. 
      } else {
        this.msalService.instance.loginRedirect();
      }
    });
2vuwiymt

2vuwiymt2#

我可以确认Paolo Cuscelas解决方案正在工作。我们在cordova InAppBrowser中使用了ionic & capacitor,因为capacitor浏览器不支持监听url更改,而这是“代理”msal路由参数所必需的。
另外,确保在azure门户中注册重定向uri。
应用程序的其余部分或多或少是基于microsoft for msal/angular包提供的示例进行设置的。

电容器的自定义导航客户端

请确保将msal交互类型设置为“InteractionType.Redirect”
构造函数要求传入InAppBrowser引用。
azure还通过#code而不是#state返回url中的数据,所以请确保相应地拆分url。

class CustomNavigationClient extends NavigationClient {

  constructor(private iab: InAppBrowser) {
    super();
  }

  async navigateExternal(url: string, options: any) {
    if (Capacitor.isNativePlatform()) {
      const browser = this.iab.create(url, '_blank', {
        location: 'yes',
        clearcache: 'yes',
        clearsessioncache: 'yes',
        hidenavigationbuttons: 'yes',
        hideurlbar: 'yes',
        fullscreen: 'yes'
      });
      browser.on('loadstart').subscribe(event => {
        if (event.url.includes('#code')) {
          // Close the in app browser and redirect to localhost + the state parameter
          browser.close();
          
          const domain = event.url.split('#')[0];
          const url = event.url.replace(domain, 'http://localhost/home');
          console.log('will redirect to:', url);
          window.location.href = url;
        }
      });
    } else {
      if (options.noHistory) {
        window.location.replace(url);
      } else {
        window.location.assign(url);
      }
    }
    return true;
  }
}

应用程序组件.ts

注册导航客户端

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx';
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
import { AuthenticationResult, EventMessage, EventType, NavigationClient } from '@azure/msal-browser';
import { Capacitor } from '@capacitor/core';
import { Subject } from 'rxjs';
import { filter, tap } from 'rxjs/operators';
import { AzureAuthService } from '@core/auth';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(
    private azureAuthService: AzureAuthService,
    private authService: MsalService,
    private msalBroadcastService: MsalBroadcastService,
    private router: Router,
    private iab: InAppBrowser,
    private msalService: MsalService,
  ) {
    this.msalService.instance.setNavigationClient(new CustomNavigationClient(this.iab));
  }

  ngOnInit(): void {

    this.msalBroadcastService.msalSubject$
      .pipe(
        filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
      )
      .subscribe((result: EventMessage) => {
        console.log('--> login success 1: ', result);
        const payload = result.payload as AuthenticationResult;
        this.authService.instance.setActiveAccount(payload.account);

        // custom service to handle authentication result within application
        this.azureAuthService.handleAuthentication(payload) 
          .pipe(
            tap(() => {
              console.log('--> login success 2: ');
              this.router.navigate(['/home']);
            })
          )
          .subscribe();
      });
  }

}

包.json

如果你使用angulas monorepo方法,请确保你把依赖项放在你的项目特定的package.json文件中,否则,当使用npx电容同步同步插件(cordova和capacitor)时,插件会被忽略。这会导致错误“... plugin_not_installed”

"dependencies": {
    ...
    "@capacitor/android": "3.4.1",
    "cordova-plugin-inappbrowser": "^5.0.0",
    "@awesome-cordova-plugins/in-app-browser": "^5.39.1",
    "@azure/msal-angular": "^2.0.1",
    "@azure/msal-browser": "^2.15.0",
    ...
}
dba5bblo

dba5bblo3#

本人确认Jazjef解决方案适用于Ionic 6,在Android上使用capacitor工作,但在IOS上需要更改event.url.replace的代码,以使用capacitor://localhost/重定向到应用程序,如果您让“http://localhost/”,它将尝试在系统浏览器上打开URL,

async navigateExternal(url: string, options: any) {
// Cortdova implementation
if (Capacitor.isNativePlatform()) {
  var browser = this.iab.create(url, '_blank', 'location=yes,clearcache=yes,clearsessioncache=yes,hidenavigationbuttons=true,hideurlbar=true,fullscreen=true');

  browser.on('loadstart').subscribe(event => {
     if (event.url.includes('#code')) {
       browser.close();
      const domain = event.url.split('#')[0];
     const url = event.url.replace(domain, 'capacitor://localhost/home');
      window.location.href = url;
    }

  });

} else {
  if (options.noHistory) {
   // window.location.replace(url);
  } else {
   // window.location.assign(url);
  }
}
return true;

}

相关问题