npm package: @rageshpikalmunde/capacitor-msal-broker
Description: Capacitor plugin for MSAL authentication with broker support for Intune/Conditional Access
Author: Ragesh Pikalmunde
Latest version: 1.0.6

# capacitor-msal-broker

A Capacitor plugin for Microsoft Authentication Library (MSAL) with **broker support** for iOS. This plugin enables authentication through Microsoft Authenticator app, which is required for Intune MDM/MAM and Conditional Access compliance.

## Features

- ✅ Native MSAL authentication on iOS
- ✅ Broker authentication via Microsoft Authenticator
- ✅ Intune/Conditional Access compliance
- ✅ Single Sign-On (SSO) support
- ✅ Silent token acquisition
- ✅ Keychain sharing for SSO across apps

## Requirements

- Capacitor 7.x
- iOS 14.0+
- Microsoft Authenticator app installed (for broker authentication)

## Installation

```bash
npm install capacitor-msal-broker
npx cap sync ios
```

## iOS Setup

### 1. Configure URL Schemes in Info.plist

Add the following to your `ios/App/App/Info.plist`:

```xml
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>msauth.$(PRODUCT_BUNDLE_IDENTIFIER)</string>
        </array>
    </dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>msauthv2</string>
    <string>msauthv3</string>
    <string>msauth</string>
</array>
```

### 2. Configure Keychain Sharing in Entitlements

Add the following to your `ios/App/App/App.entitlements`:

```xml
<key>keychain-access-groups</key>
<array>
    <string>$(AppIdentifierPrefix)com.microsoft.adalcache</string>
</array>
```

### 3. Azure Portal Configuration

In your Azure AD App Registration:

1. Go to **Authentication** → **Platform configurations** → **iOS/macOS**
2. Add your Bundle ID
3. Configure the redirect URI: `msauth.<your-bundle-id>://auth`

## Usage

### Initialize

```typescript
import { MsalBroker } from 'capacitor-msal-broker';

await MsalBroker.initialize({
  clientId: 'your-azure-client-id',
  tenant: 'organizations', // or specific tenant ID
  authorityUrl: 'https://login.microsoftonline.com/organizations',
  scopes: ['User.Read', 'email'], // Don't include openid, profile, offline_access
  redirectUri: 'msauth.com.your.bundleid://auth',
  brokerEnabled: true,
  keychainGroup: 'com.microsoft.adalcache',
});
```

> **Important**: Do not include `openid`, `profile`, or `offline_access` in scopes - MSAL iOS adds these automatically.

### Login (Interactive)

```typescript
try {
  const result = await MsalBroker.login();
  console.log('Access Token:', result.accessToken);
  console.log('User:', result.account.username);
} catch (error) {
  console.error('Login failed:', error);
}
```

### Get Accounts

```typescript
const { accounts } = await MsalBroker.getAccounts();
if (accounts.length > 0) {
  console.log('Found account:', accounts[0].username);
}
```

### Acquire Token Silently

```typescript
try {
  const result = await MsalBroker.acquireTokenSilently({
    identifier: account.identifier,
    forceRefresh: false,
  });
  console.log('Token:', result.accessToken);
} catch (error) {
  // Silent auth failed, use interactive login
  const result = await MsalBroker.login();
}
```

### Logout

```typescript
await MsalBroker.logout();
```

## API Reference

### `initialize(options: MsalBrokerInitOptions): Promise<{ success: boolean }>`

Initialize MSAL with your configuration.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| clientId | string | Yes | Azure AD Application (client) ID |
| tenant | string | No | Tenant ID or 'organizations'/'common' |
| authorityUrl | string | No | Authority URL |
| scopes | string[] | No | OAuth scopes to request |
| redirectUri | string | No | Redirect URI configured in Azure |
| brokerEnabled | boolean | No | Enable broker authentication (default: true) |
| keychainGroup | string | No | Keychain group for SSO |
| knownAuthorities | string[] | No | List of known authorities |

### `login(options?: MsalBrokerLoginOptions): Promise<MsalBrokerAuthResult>`

Perform interactive login.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| loginHint | string | No | Pre-fill username |
| prompt | string | No | Prompt behavior: 'selectAccount', 'login', 'consent', 'none' |

### `acquireTokenSilently(options: MsalBrokerSilentOptions): Promise<MsalBrokerAuthResult>`

Acquire token silently for an existing account.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| identifier | string | Yes | Account identifier |
| forceRefresh | boolean | No | Force token refresh |

### `getAccounts(): Promise<{ accounts: MsalBrokerAccount[] }>`

Get all cached accounts.

### `logout(): Promise<{ success: boolean }>`

Sign out and clear cached tokens.

## Types

```typescript
interface MsalBrokerAuthResult {
  accessToken: string;
  idToken?: string;
  expiresOn: string;
  scopes: string[];
  account: MsalBrokerAccount;
}

interface MsalBrokerAccount {
  identifier: string;
  username: string;
  tenantId?: string;
  name?: string;
}
```

## Troubleshooting

### Error -50000: Reserved scopes

Remove `openid`, `profile`, and `offline_access` from your scopes array. MSAL iOS adds these automatically.

### Error -51112: Broker not responding

1. Ensure Microsoft Authenticator is installed
2. Verify your redirect URI matches Azure Portal configuration
3. Check that keychain sharing is properly configured

### Keychain errors (-25300)

Ensure your entitlements include the correct keychain access groups with `$(AppIdentifierPrefix)` prefix.

## License

MIT