快速入门:从 iOS 应用将用户登录并调用 Microsoft Graph APIQuickstart: Sign in users and call the Microsoft Graph API from an iOS app

适用于:Applies to:
  • Azure AD v1.0 终结点Azure AD v1.0 endpoint
  • Azure Active Directory Authentication Library (ADAL)Azure Active Directory Authentication Library (ADAL)

对于需要访问受保护资源的 iOS 客户端,Azure Active Directory (Azure AD) 提供了 Active Directory 身份验证库 (ADAL)。Azure Active Directory (Azure AD) provides the Active Directory Authentication Library (ADAL) for iOS clients that need to access protected resources. ADAL 简化了应用用来获取访问令牌的过程。ADAL simplifies the process that your app uses to get access tokens.

在本快速入门中,你将构建具有以下功能的一个 Objective C 待办事项列表应用程序:In this quickstart, you'll build an Objective C To-Do List application that:

  • 使用 OAuth 2.0 身份验证协议获取调用 Azure AD 图形 API 的访问令牌Gets access tokens for calling the Azure AD Graph API by using the OAuth 2.0 authentication protocol
  • 在目录中搜索具有给定别名的用户Searches a directory for users with a given alias

要构建完整的工作应用程序,需要:To build the complete, working application, you'll need to:

  1. 将应用程序注册到 Azure AD。Register your application with Azure AD.
  2. 安装并配置 ADAL。Install and configure ADAL.
  3. 使用 ADAL 从 Azure AD 获取令牌。Use ADAL to get tokens from Azure AD.

先决条件Prerequisites

开始前,请完成这些先决条件:To get started, complete these prerequisites:

Tip

试用开发人员门户,只需花费几分钟时间,它就能帮助你开始使用 Azure AD。Try the developer portal to get up and running with Azure AD in just a few minutes. 开发人员门户可指导完成注册应用并将 Azure AD 集成到代码的整个过程。The developer portal guides you through the process of registering an app and integrating Azure AD into your code. 完成上述过程后,会获得一个可对租户中的用户进行身份验证的简单应用程序,以及一个可以接受令牌并执行验证的后端。When you’re finished, you'll have a simple application that can authenticate users in your tenant, and a back end that can accept tokens and perform validation.

步骤 1:确定用于 iOS 的重定向 URIStep 1: Determine what your redirect URI is for iOS

若要安全地在特定 SSO 方案中启动应用程序,必须以特定格式创建重定向 URI 。To securely start your applications in certain SSO scenarios, you must create a redirect URI in a particular format. 重定向 URI 用于确保将令牌返回给需要它们的正确应用程序。A redirect URI is used to make sure that the tokens return to the correct application that asked for them.

重定向 URI 的 iOS 格式为:The iOS format for a redirect URI is:

<app-scheme>://<bundle-id>
  • app-scheme - 在 XCode 项目中注册的,它是其他应用程序的调用方式。app-scheme - Is registered in your XCode project and is how other applications can call you. 可以在 Info.plist > URL types > URL Identifier 下找到 app-scheme。You can find app-scheme under Info.plist > URL types > URL Identifier. 如果尚未配置一个或多个 app-scheme,请创建一个。Create an app-scheme if you don't already have one or more configured.
  • bundle-id - 是捆绑标识符,位于 XCode 项目设置中的“标识符”下。 bundle-id - Is the Bundle Identifier found under identity in your XCode project settings.

此快速入门代码的示例:An example for this quickstart code:

msquickstart://com.microsoft.azureactivedirectory.samples.graph.QuickStartmsquickstart://com.microsoft.azureactivedirectory.samples.graph.QuickStart

步骤 2:注册 DirectorySearcher 应用程序Step 2: Register the DirectorySearcher application

若要设置应用来获取令牌,需要在 Azure AD 租户中注册该应用,并授予其访问 Azure AD 图形 API 的权限:To set up your app to get tokens, you need to register the app in your Azure AD tenant and grant it permission to access the Azure AD Graph API.

  1. 登录到 Azure 门户Sign in to the Azure portal.
  2. 在顶部栏中选择帐户。On the top bar, select your account. 在“目录” 列表下选择要注册应用程序的 Active Directory 租户。Under the Directory list, choose the Active Directory tenant where you want to register your application.
  3. 在最左侧的导航窗格中选择“所有服务”,并选择“Azure Active Directory”。 Select All services in the leftmost navigation pane, and then select Azure Active Directory.
  4. 选择“应用注册”,然后选择“新建注册” 。Select App registrations, and then select New registration.
  5. 根据提示创建新的客户端应用程序。Follow the prompts to create a new client application.
    • 名称是应用程序名称,它向最终用户描述该应用程序。Name is the application name and describes your application to end users.
    • 重定向 URI 是 Azure AD 用来返回令牌响应的方案与字符串组合。Redirect URI is a scheme and string combination that Azure AD uses to return token responses. 请输入特定于应用程序并基于之前的重定向 URI 信息的一个值。Enter a value that is specific to your application and is based on the previous redirect URI information. 另外,请从下拉列表中选择“公共客户端(移动和桌面)” 。Also select Public client (mobile and desktop) from the dropdown.
  6. 完成注册后,Azure AD 将为应用分配一个唯一的应用程序 ID。After you've completed the registration, Azure AD assigns your app a unique application ID. 在后面的部分中会用到此值,因此,请从应用程序选项卡中复制此值。You'll need this value in the next sections, so copy it from the application tab.
  7. 在“API 权限” 页面上,选择“添加权限” 。From the API permissions page, select Add a permission. 在“选择 API”中选择“Microsoft Graph” 。Inside Select an API select Microsoft Graph.
  8. 在“委托的权限” 下,选择 User.Read 权限,然后点击“添加” 以保存。Under Delegated permissions, select the permission User.Read, then hit Add to save. 此权限将应用程序设置为在 Azure AD Graph API 中查询用户。This permission sets up your application to query the Azure AD Graph API for users.

步骤 3:安装并配置 ADALStep 3: Install and configure ADAL

将应用程序注册到 Azure AD 后,可以安装 ADAL 并编写标识相关的代码。Now that you have an application in Azure AD, you can install ADAL and write your identity-related code. 为了使 ADAL 能够与 Azure AD 进行通信,需要为 ADAL 提供一些有关应用注册的信息。For ADAL to communicate with Azure AD, you need to provide it with some information about your app registration.

  1. 首先,使用 CocoaPods 将 ADAL 添加到 DirectorySearcher 项目。Begin by adding ADAL to the DirectorySearcher project by using CocoaPods.

    $ vi Podfile
    
  2. 将以下内容添加到 podfile:Add the following to this podfile:

    source 'https://github.com/CocoaPods/Specs.git'
    link_with ['QuickStart']
    xcodeproj 'QuickStart'
    
    pod 'ADAL'
    
  3. 使用 CocoaPods 加载 podfile。Load the podfile by using CocoaPods. 此步骤将创建你加载的新 XCode 工作区。This step creates a new XCode workspace that you load.

    $ pod install
    ...
    $ open QuickStart.xcworkspace
    
  4. 在快速入门项目中,打开 plist 文件 settings.plistIn the QuickStart project, open the plist file settings.plist.

  5. 替换节中的元素值,以使用你在 Azure 门户中输入的值。Replace the values of the elements in the section to use the same values that you entered in the Azure portal. 每当使用 ADAL 时,代码都会引用这些值。Your code references these values whenever it uses ADAL.

    • tenant 是 Azure AD 租户的域,例如,contoso.partner.onmschina.cn。tenant is the domain of your Azure AD tenant, for example, contoso.partner.onmschina.cn.
    • clientId 是从门户复制的应用程序的客户端 ID。clientId is the client ID of your application that you copied from the portal.
    • redirectUri 是你在门户中注册的重定向 URL。redirectUri is the redirect URL that you registered in the portal.

步骤 4:使用 ADAL 从 Azure AD 获取令牌Step 4: Use ADAL to get tokens from Azure AD

ADAL 遵守的基本原理是,每当应用程序需要访问令牌时,它只需调用 completionBlock +(void) getToken :,ADAL 就会负责其余的工作。The basic principle behind ADAL is that whenever your app needs an access token, it simply calls a completionBlock +(void) getToken :, and ADAL does the rest.

  1. QuickStart 项目中,打开 GraphAPICaller.m 并找到靠近顶部位置的 // TODO: getToken for generic Web API flows. Returns a token with no additional parameters provided. 注释。In the QuickStart project, open GraphAPICaller.m and locate the // TODO: getToken for generic Web API flows. Returns a token with no additional parameters provided. comment near the top.

    将在此处通过 CompletionBlock 传递 ADAL 与 Azure AD 通信时所需的坐标,并告诉 ADAL 如何缓存令牌。This is where you pass ADAL the coordinates through a CompletionBlock, to communicate with Azure AD, and tell it how to cache tokens.

    +(void) getToken : (BOOL) clearCache
               parent:(UIViewController*) parent
    completionHandler:(void (^) (NSString*, NSError*))completionBlock;
    {
        AppData* data = [AppData getInstance];
        if(data.userItem){
            completionBlock(data.userItem.accessToken, nil);
            return;
        }
    
        ADAuthenticationError *error;
        authContext = [ADAuthenticationContext authenticationContextWithAuthority:data.authority error:&error];
        authContext.parentController = parent;
        NSURL *redirectUri = [[NSURL alloc]initWithString:data.redirectUriString];
    
        [ADAuthenticationSettings sharedInstance].enableFullScreen = YES;
        [authContext acquireTokenWithResource:data.resourceId
                                     clientId:data.clientId
                                  redirectUri:redirectUri
                               promptBehavior:AD_PROMPT_AUTO
                                       userId:data.userItem.userInformation.userId
                        extraQueryParameters: @"nux=1" // if this strikes you as strange it was legacy to display the correct mobile UX. You most likely won't need it in your code.
                             completionBlock:^(ADAuthenticationResult *result) {
    
                                  if (result.status != AD_SUCCEEDED)
                                  {
                                     completionBlock(nil, result.error);
                                  }
                                  else
                                  {
                                      data.userItem = result.tokenCacheStoreItem;
                                      completionBlock(result.tokenCacheStoreItem.accessToken, nil);
                                  }
                             }];
    }
    
    
  2. 你需要使用此令牌在图形中搜索用户。You need to use this token to search for users in the graph. 找到 // TODO: implement SearchUsersList 注释。Find the // TODO: implement SearchUsersList comment. 此方法向 Azure AD 图形 API 发出 GET 请求,以查询其 UPN 以给定搜索词开头的用户。This method makes a GET request to the Azure AD Graph API to query for users whose UPN begins with the given search term.

    要查询 Azure AD Graph API,需要在请求的 Authorization 标头中包含 access_token。To query the Azure AD Graph API, you need to include an access_token in the Authorization header of the request. 这是 ADAL 的进入位置。This is where ADAL comes in.

    +(void) searchUserList:(NSString*)searchString
                    parent:(UIViewController*) parent
          completionBlock:(void (^) (NSMutableArray* Users, NSError* error)) completionBlock
    {
        if (!loadedApplicationSettings)
       {
            [self readApplicationSettings];
        }
    
        AppData* data = [AppData getInstance];
    
        NSString *graphURL = [NSString stringWithFormat:@"%@%@/users?api-version=%@&$filter=startswith(userPrincipalName, '%@')", data.taskWebApiUrlString, data.tenant, data.apiversion, searchString];
    
        [self craftRequest:[self.class trimString:graphURL]
                    parent:parent
         completionHandler:^(NSMutableURLRequest *request, NSError *error) {
    
             if (error != nil)
             {
                 completionBlock(nil, error);
             }
             else
             {
    
                 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
                 [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    
                     if (error == nil && data != nil){
    
                         NSDictionary *dataReturned = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
    
                         // We can grab the JSON node at the top to get our graph data.
                         NSArray *graphDataArray = [dataReturned objectForKey:@"value"];
    
                         // Don't be thrown off by the key name being "value". It really is the name of the
                         // first node. :-)
    
                         // Each object is a key value pair
                         NSDictionary *keyValuePairs;
                         NSMutableArray* Users = [[NSMutableArray alloc]init];
    
                         for(int i =0; i < graphDataArray.count; i++)
                         {
                             keyValuePairs = [graphDataArray objectAtIndex:i];
    
                             User *s = [[User alloc]init];
                             s.upn = [keyValuePairs valueForKey:@"userPrincipalName"];
                             s.name =[keyValuePairs valueForKey:@"givenName"];
    
                             [Users addObject:s];
                         }
    
                         completionBlock(Users, nil);
                     }
                     else
                     {
                         completionBlock(nil, error);
                     }
    
                }];
             }
         }];
    
    }
    
    
  3. 当应用通过调用 getToken(...) 来请求令牌时,ADAL 会尝试返回一个令牌,而不要求用户输入凭据。When your app requests a token by calling getToken(...), ADAL attempts to return a token without asking the user for credentials. 如果 ADAL 确定用户需要登录以获取令牌,它会显示一个用于登录的对话框,收集用户的凭据,并在身份验证成功后返回一个令牌。If ADAL determines that the user needs to sign in to get a token, it will display a dialog box for sign-in, collect the user's credentials, and then return a token after successful authentication. 如果 ADAL 出于任何原因无法返回令牌,则会引发 AdalExceptionIf ADAL is not able to return a token for any reason, it throws an AdalException.

Note

AuthenticationResult 对象包含一个 tokenCacheStoreItem 对象,后者可用于收集应用可能需要的信息。The AuthenticationResult object contains a tokenCacheStoreItem object that you can use to collect the information that your app may need. 在此快速入门中,tokenCacheStoreItem 用于确定身份验证是否已发生。In the QuickStart, tokenCacheStoreItem is used to determine if authentication is already done.

步骤 5:生成并运行应用程序Step 5: Build and run the application

祝贺!Congratulations! 现在,已有了一个可正常工作的 iOS 应用程序,它可以对用户进行身份验证,使用 OAuth 2.0 安全地调用 Web API,并获取有关用户的基本信息。You now have a working iOS application that can authenticate users, securely call Web APIs by using OAuth 2.0, and get basic information about the user.

如果尚未这样做,可以在租户中填充一些用户。If you haven't already, now is the time to populate your tenant with some users.

  1. 启动快速入门应用,并使用这些用户之一进行登录。Start your QuickStart app, and then sign in with one of those users.
  2. 根据用户的 UPN 搜索其他用户。Search for other users based on their UPN.
  3. 关闭应用,然后重新启动它。Close the app, and then start it again. 请注意,用户的会话将保持不变。Notice that the user's session remains intact.

使用 ADAL 可以方便地将所有这些常见标识功能合并到应用程序中。ADAL makes it easy to incorporate all of these common identity features into your application. 它会负责所有的繁琐工作,例如缓存管理、OAuth 协议支持、向用户显示用于登录的 UI,以及刷新过期令牌。It takes care of all the dirty work for you, like cache management, OAuth protocol support, presenting the user with a UI to sign in, and refreshing expired tokens. 只需要真正了解一个 API 调用,即 getTokenAll you really need to know is a single API call, getToken.

GitHub 上提供了一个完整示例(不包含配置值)供参考。For reference, the completed sample (without your configuration values) is provided on GitHub.

后续步骤Next steps

现在,可以转到其他方案。You can now move on to additional scenarios. 我们建议尝试以下后续操作:We suggest trying these next: