Car*_*los 7 azure-active-directory ionic-framework azure-ad-msal ionic-native msal-angular
我有一个 Ionic 应用程序需要在 Azure 中进行身份验证,因此我按照本教程安装了 MSAL: https: //learn.microsoft.com/en-us/graph/tutorials/angular
它的工作原理就像“离子服务”的魅力,但当我在设备中运行它时,当我尝试登录 Azure 时它会崩溃。我认为这是因为 MSAL 显示的弹出窗口在 Ionic 中不允许登录。
所以我的第一次尝试是将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();
}
}
Run Code Online (Sandbox Code Playgroud)
我添加了这个新的(基于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();
}
}
Run Code Online (Sandbox Code Playgroud)
但这样,用户信息不会保存,因为我没有“结果”来处理或调用 setActiveAccount(result)。即使在“离子服务”中它也不起作用,所以我放弃了这种方法。
第二种方法是在寻找可能的解决方案两天后,在 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();\
}"
});
}
Run Code Online (Sandbox Code Playgroud)
但它只是打开一个新窗口,什么也不做,它不执行loginPopup(),所以我也放弃了第二种方法。
有人知道如何避免 Ionic 中的弹出问题吗?
谢谢
我可以确认保罗·库塞拉斯的解决方案正在发挥作用。我们在 cordova InAppBrowser 中使用 ionic 和电容器,因为电容器浏览器不支持监听 url 更改,而这是“代理”msal 路由参数所必需的。
另外,请确保在 Azure 门户中注册重定向 URI。
应用程序的其余部分或多或少是根据微软为 msal/Angular 包提供的示例进行设置的。
Capacitor 的 CustomNavigationClient
确保将 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;
}
}
Run Code Online (Sandbox Code Playgroud)
app.component.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();
});
}
}
Run Code Online (Sandbox Code Playgroud)
package.json
如果您使用 Angulars monorepo 方法,请确保将依赖项放置在项目特定的 package.json 文件中,否则,当使用 npx capsync 同步插件(cordova 和电容器)时,插件将被忽略。这会导致错误“...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",
...
}
Run Code Online (Sandbox Code Playgroud)
小智 4
我设法通过使用自定义导航客户端使用 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;
}
}
Run Code Online (Sandbox Code Playgroud)
应用程序组件.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();
}
});
Run Code Online (Sandbox Code Playgroud)