如何:macOS 和 iOS 上的 ADAL 与 MSAL 应用之间的 SSOHow to: SSO between ADAL and MSAL apps on macOS and iOS

适用于 iOS 的 Microsoft 身份验证库 (MSAL) 可与 ADAL Objective-C 共享应用程序之间的 SSO 状态。The Microsoft Authentication Library (MSAL) for iOS can share SSO state with ADAL Objective-C between applications. 你可以按照自己的步调将应用迁移到 MSAL,确保用户仍可受益于跨应用 SSO - 即使混用基于 ADAL 和基于 MSAL 的应用。You can migrate your apps to MSAL at your own pace, ensuring that your users will still benefit from cross-app SSO--even with a mix of ADAL and MSAL-based apps.

如果你正在寻找有关使用 MSAL SDK 在应用之间设置 SSO 的指导,请参阅多个应用之间的静默 SSOIf you're looking for guidance of setting up SSO between apps using the MSAL SDK, see Silent SSO between multiple apps. 本文重点介绍 ADAL 与 MSAL 之间的 SSO。This article focuses on SSO between ADAL and MSAL.

实现 SSO 的具体步骤取决于所用的 ADAL 版本。The specifics implementing SSO depend on ADAL version that you're using.

ADAL 2.7.xADAL 2.7.x

本部分介绍 MSAL 与 ADAL 2.7.x 之间的 SSO 差异This section covers SSO differences between MSAL and ADAL 2.7.x

缓存格式Cache format

ADAL 2.7.x 可以读取 MSAL 缓存格式。ADAL 2.7.x can read the MSAL cache format. 对于版本 ADAL 2.7.x 的跨应用 SSO,无需执行任何特殊操作。You don't need to do anything special for cross-app SSO with version ADAL 2.7.x. 但是,需要注意这两个库支持的帐户标识符的差异。However, you need to be aware of differences in account identifiers that those two libraries support.

帐户标识符差异Account identifier differences

MSAL 和 ADAL 使用不同的帐户标识符。MSAL and ADAL use different account identifiers. ADAL 使用 UPN 作为主要帐户标识符。ADAL uses UPN as its primary account identifier. MSAL 使用一个不可显示的帐户标识符,该标识符基于 AAD 帐户的对象 ID 和租户 ID,以及其他帐户类型的 sub 声明。MSAL uses a non-displayable account identifier that is based of an object ID and a tenant ID for AAD accounts, and a sub claim for other types of accounts.

在 MSAL 结果中收到 MSALAccount 对象时,其 identifier 属性会包含一个帐户标识符。When you receive an MSALAccount object in the MSAL result, it contains an account identifier in the identifier property. 应用程序应使用此标识符发出后续的静默请求。The application should use this identifier for subsequent silent requests.

identifier 以外,MSALAccount 对象还包含名为 username 的可显示标识符。In addition to identifier, MSALAccount object contains a displayable identifier called username. 该标识符将转换为 ADAL 中的 userIdThat translates to userId in ADAL. username 不被视为唯一标识符且随时可能会更改,因此,只应将其用于 ADAL 的后向兼容方案。username is not considered a unique identifier and can change anytime, so it should only be used for backward compatibility scenarios with ADAL. MSAL 支持使用 usernameidentifier 的缓存查询,建议按 identifier 进行查询。MSAL supports cache queries using either username or identifier, where querying by identifier is recommended.

下表汇总了 ADAL 与 MSAL 之间的帐户标识符差异:Following table summarizes account identifier differences between ADAL and MSAL:

帐户标识符Account identifier MSALMSAL ADAL 2.7.xADAL 2.7.x 旧版 ADAL(ADAL 2.7.x 以前的版本)Older ADAL (before ADAL 2.7.x)
可显示的标识符displayable identifier username userId userId
不可显示的唯一标识符unique non-displayable identifier identifier homeAccountId 不适用N/A
没有任何已知的帐户 IDNo account id known 通过 allAccounts: 中的 MSALPublicClientApplication API 查询所有帐户Query all accounts through allAccounts: API in MSALPublicClientApplication 不适用N/A 不适用N/A

这是提供这些标识符的 MSALAccount 接口:This is the MSALAccount interface providing those identifiers:

@protocol MSALAccount <NSObject>

/*!
 Displayable user identifier. Can be used for UI and backward compatibility with ADAL.
 */
@property (readonly, nullable) NSString *username;

/*!
 Unique identifier for the account.
 Save this for account lookups from cache at a later point.
 */
@property (readonly, nullable) NSString *identifier;

/*!
 Host part of the authority string used for authentication based on the issuer identifier.
 */
@property (readonly, nonnull) NSString *environment;

/*!
 ID token claims for the account.
 Can be used to read additional information about the account, e.g. name
 Will only be returned if there has been an id token issued for the client Id for the account's source tenant.
 */
@property (readonly, nullable) NSDictionary<NSString *, NSString *> *accountClaims;

@end

从 MSAL 到 ADAL 的 SSOSSO from MSAL to ADAL

如果你有一个 MSAL 应用和一个 ADAL 应用,并且用户是首次登录到基于 MSAL 的应用,则你可以通过保存 MSALAccount 对象中的 username 并将其作为 userId 传递给基于 ADAL 的应用,在 ADAL 应用中实现 SSO。If you have an MSAL app and an ADAL app, and the user first signs into the MSAL-based app, you can get SSO in the ADAL app by saving the username from the MSALAccount object and passing it to your ADAL-based app as userId. 然后,ADAL 可以使用 acquireTokenSilentWithResource:clientId:redirectUri:userId:completionBlock: API 以静默方式查找帐户信息。ADAL can then find the account information silently with the acquireTokenSilentWithResource:clientId:redirectUri:userId:completionBlock: API.

从 ADAL 到 MSAL 的 SSOSSO from ADAL to MSAL

如果你有一个 MSAL 应用和一个 ADAL 应用,并且用户是首次登录到基于 ADAL 的应用,则你可以使用 ADAL 用户标识符在 MSAL 中执行帐户查找。If you have an MSAL app and an ADAL app, and the user first signs into the ADAL-based app, you can use ADAL user identifiers for account lookups in MSAL. 此方法在从 ADAL 迁移到 MSAL 时也适用。This also applies when migrating from ADAL to MSAL.

ADAL 的 homeAccountIdADAL's homeAccountId

ADAL 2.7.x 通过此属性在结果中返回 ADUserInformation 对象中的 homeAccountIdADAL 2.7.x returns the homeAccountId in the ADUserInformation object in the result via this property:

/*! Unique AAD account identifier across tenants based on user's home OID/home tenantId. */
@property (readonly) NSString *homeAccountId;

ADAL 中的 homeAccountId 等效于 MSAL 中的 identifierhomeAccountId in ADAL's is equivalent of identifier in MSAL. 可以保存此标识符,以便在 MSAL 中使用它来通过 accountForIdentifier:error: API 执行帐户查找。You can save this identifier to use in MSAL for account lookups with the accountForIdentifier:error: API.

ADAL 的 userIdADAL's userId

如果 homeAccountId 不可用,或者只有可显示的标识符,则可以使用 ADAL 的 userId 在 MSAL 中执行帐户查找。If homeAccountId is not available, or you only have the displayable identifier, you can use ADAL's userId to lookup the account in MSAL.

在 MSAL 中,首先按 usernameidentifier 查找帐户。In MSAL, first look up an account by username or identifier. 如果有 identifier,请始终使用它执行查询,仅将 username 用作应变手段。Always use identifier for querying if you have it, and only use username as a fallback. 如果找到帐户,请在 acquireTokenSilent 调用中使用该帐户。If the account is found, use the account in the acquireTokenSilent calls.

Objective-C:Objective-C:

NSString *msalIdentifier = @"previously.saved.msal.account.id";
MSALAccount *account = nil;
    
if (msalIdentifier)
{
    // If you have MSAL account id returned either from MSAL as identifier or ADAL as homeAccountId, use it
    account = [application accountForIdentifier:@"my.account.id.here" error:nil];
}
else
{
    // Fallback to ADAL userId for migration
    account = [application accountForUsername:@"adal.user.id" error:nil];
}
    
if (!account)
{
  // Account not found.
  return;
}

MSALSilentTokenParameters *silentParameters = [[MSALSilentTokenParameters alloc] initWithScopes:@[@"https://microsoftgraph.chinacloudapi.cn/user.read"] account:account];
[application acquireTokenSilentWithParameters:silentParameters completionBlock:completionBlock];

Swift:Swift:

        
let msalIdentifier: String?
var account: MSALAccount
        
do {
  if let msalIdentifier = msalIdentifier {
    account = try application.account(forIdentifier: msalIdentifier)
  }
  else {
    account = try application.account(forUsername: "adal.user.id") 
  }
             
  let silentParameters = MSALSilentTokenParameters(scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"], account: account)          
  application.acquireTokenSilent(with: silentParameters) {
    (result: MSALResult?, error: Error?) in
    // handle result
  }  
} catch let error as NSError {
  // handle error or account not found
}

MSAL 支持的帐户查找 API:MSAL supported account lookup APIs:

/*!
 Returns account for the given account identifier (received from an account object returned in a previous acquireToken call)
 
 @param  error      The error that occurred trying to get the accounts, if any, if you're
                    not interested in the specific error pass in nil.
 */
- (nullable MSALAccount *)accountForIdentifier:(nonnull NSString *)identifier
                                         error:(NSError * _Nullable __autoreleasing * _Nullable)error;
    
/*!
Returns account for for the given username (received from an account object returned in a previous acquireToken call or ADAL)
    
@param  username    The displayable value in UserPrincipleName(UPN) format
@param  error       The error that occurred trying to get the accounts, if any, if you're
                    not interested in the specific error pass in nil.
*/
- (MSALAccount *)accountForUsername:(NSString *)username
                              error:(NSError * __autoreleasing *)error;

ADAL 2.x-2.6.6ADAL 2.x-2.6.6

本部分介绍 MSAL 与 ADAL 2.x-2.6.6 之间的 SSO 差异This section covers SSO differences between MSAL and ADAL 2.x-2.6.6.

旧版 ADAL 原生并不支持 MSAL 缓存格式。Older ADAL versions don't natively support the MSAL cache format. 但是,为了确保从 ADAL 平稳迁移到 MSAL,MSAL 可以读取旧版 ADAL 缓存格式,而不会再次提示输入用户凭据。However, to ensure smooth migration from ADAL to MSAL, MSAL can read the older ADAL cache format without prompting for user credentials again.

由于 homeAccountId 在旧版 ADAL 中不可用,因此需要使用 username 查找帐户:Because homeAccountId isn't available in older ADAL versions, you'd need to look up accounts using the username:

/*!
 Returns account for for the given username (received from an account object returned in a previous acquireToken call or ADAL)

 @param  username    The displayable value in UserPrincipleName(UPN) format
 @param  error       The error that occurred trying to get the accounts, if any.  If you're not interested in the specific error pass in nil.
 */
- (MSALAccount *)accountForUsername:(NSString *)username
                              error:(NSError * __autoreleasing *)error;

例如:For example:

Objective-C:Objective-C:

MSALAccount *account = [application accountForUsername:@"adal.user.id" error:nil];;
MSALSilentTokenParameters *silentParameters = [[MSALSilentTokenParameters alloc] initWithScopes:@[@"https://microsoftgraph.chinacloudapi.cn/user.read"] account:account];
[application acquireTokenSilentWithParameters:silentParameters completionBlock:completionBlock];

Swift:Swift:

do {
  let account = try application.account(forUsername: "adal.user.id")          
  let silentParameters = MSALSilentTokenParameters(scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"], account: account)
  application.acquireTokenSilent(with: silentParameters) { 
    (result: MSALResult?, error: Error?) in
    // handle result
  }   
} catch let error as NSError { 
  // handle error or account not found
}

或者,可以读取所有帐户,这样也会读取 ADAL 中的帐户信息:Alternatively, you can read all of the accounts, which will also read account information from ADAL:

Objective-C:Objective-C:

NSArray *accounts = [application allAccounts:nil];
    
if ([accounts count] == 0)
{
  // No account found.
  return; 
}
if ([accounts count] > 1)
{
  // You might want to display an account picker to user in actual application
  // For this sample we assume there's only ever one account in cache
  return;
}
    ``
MSALSilentTokenParameters *silentParameters = [[MSALSilentTokenParameters alloc] initWithScopes:@[@"https://microsoftgraph.chinacloudapi.cn/user.read"] account:accounts[0]];
[application acquireTokenSilentWithParameters:silentParameters completionBlock:completionBlock];

Swift:Swift:

      
do {
  let accounts = try application.allAccounts()
  if accounts.count == 0 {
    // No account found.
    return
  }
  if accounts.count > 1 {
    // You might want to display an account picker to user in actual application
    // For this sample we assume there's only ever one account in cache
    return
  }
  
  let silentParameters = MSALSilentTokenParameters(scopes: ["https://microsoftgraph.chinacloudapi.cn/user.read"], account: accounts[0])
  application.acquireTokenSilent(with: silentParameters) {
    (result: MSALResult?, error: Error?) in
    // handle result or error  
  }  
} catch let error as NSError { 
  // handle error
}

后续步骤Next steps

详细了解身份验证流和应用程序方案Learn more about Authentication flows and application scenarios