Help us understand the problem. What is going on with this article?

Sign In with AppleでAWSサービスに接続する

はじめに

こんにちは。Daddy's Officeの市川です。

先日、「LiveCapture3 Remote」のiOS版をリリースしました。
今回は開発にXamarin.Formsを使用して、Android/iOS両対応の開発を行いました。

「LiveCapture3 Remote」では、クラウドサーバにAWSを使用したアプリケーションですが、Android版のアプリサインインは、Googleアカウントでサインインし、AWS Cognitoでユーザに対して適切なIAM権限を付与する形で実現しています。

Googleアカウントでのサインインに関してはこちらを参照してください。
GoogleアカウントでAWSサービスに接続する

今回iOS版では、必須になっているSign In with Appleでサインインし、Android版と同様に、AWS Cognitoで適切なIAM権限をアプリに付与してAWSの各種サービスに直接アクセスできるようにしました。

今回は、このやり方を説明しようと思います。

ちなみに「LiveCapture3 Remote」はiOS13以降のみ対応なので、iOS12以前の説明は行いません。(というか分かりません。。。)

Sign In with Appleの有効化

まず、Apple Developerで、Sign In with Appleを有効にします。

Apple Developerを開き、(アプリのApp IDを登録していない場合は登録して)、「Certificates, Identifiers & Profiles」で対象アプリを選択します。

AppDeveloper01.jpg

設定の「Capabilities」をスクロールすると「Sign In with Apple」がありますので、チェックを付けます。

AppDeveloper02.jpg

あとは、プロジェクトのEntitlements.plistに「com.apple.developer.applesignin」のキーを下記のように追加します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.applesignin</key>
    <array>
        <string>Default</string>
    </array>
</dict>
</plist>

IDプロバイダーの追加(AWS)

Apple IDアカウントで認証済みのアプリからのアクセスに対して、AWSサービスへアクセスするAWS認証情報を発行するために、CognitoのIDプールとApple IDアカウントを連携させます。

IAMの「IDプロバイダー」を選択して、「プロバイダの作成」ボタンをクリックします。

IAM01.jpg

項目 入力値
プロバイダーのタイプ OpenID Connect
プロバイダーのURL https:///appleid.apple.com
対象者 App Developerで設定したアプリのIdentifier(com.xxxxx.xxxxx)

上記を入力して、IDプロバイダー(appleid.apple.com)を作成します。

Cognito IDプールの作成(AWS)

OpenIDのIDプロバイダを追加すると、CognitoのIDプール作成画面のOpenIDのタブに、今追加した「appleid.apple.com」が選択肢として表示されますので、チェックを入れます。

Cognito01.jpg

今回は認証されていないIDのアクセスは拒否しますので、そのままIDプールを作成します。

AWS認証情報の権限設定

CognitoでIDプールを作成すると、2つのIAMロールが生成されます。

  • Cognito_xxxxxxAuth_Role (認証済みユーザ用のIAMロール)
  • Cognito_xxxxxxUnauth_Role(認証されていないユーザ用のIAMロール)

(xxxxxxは作成したIDプールの名称です)

先ほどのIDプール作成で、「認証されていないIDに対してのアクセスを有効にする」にチェックを入れると、認証していないユーザにもAWSサービスへのアクセスを許可することができます。

ただ、今回は使用しませんので、認証済みユーザ向けの「Cognito_xxxxxxxAuth_Role」の方に、必要なアクセス権を設定します。

実装

以下を参考に実装します。
Xamarin.iOS Appleでのサインイン

今回はXamarin.Formsを使用して開発していますので、Sign In with Appleの処理はDependency Serviceとして実装します。
(Android側はGoogle SignInの処理を記述しました)

iOS側のDependency Serviceは以下のような感じです。

[assembly: Xamarin.Forms.Dependency(typeof(AppleSignInService))]
namespace lc3remote.iOS.Services
{
    public class AppleSignInService: NSObject, ISignInService,IASAuthorizationControllerDelegate, IASAuthorizationControllerPresentationContextProviding
    {
        AuthManager authManager;

        public async Task<SignInUser> SignInAsync()
        {
            var appleIdProvider = new ASAuthorizationAppleIdProvider();
            var request = appleIdProvider.CreateRequest();
            request.RequestedScopes = new[] { ASAuthorizationScope.Email, ASAuthorizationScope.FullName };

            authManager = new AuthManager(UIApplication.SharedApplication.KeyWindow);

            var authorizationController = new ASAuthorizationController(new[] { request });
            authorizationController.Delegate = authManager;
            authorizationController.PresentationContextProvider = authManager;
            authorizationController.PerformRequests();

            var creds = await authManager.Credentials;

            if (creds == null)
                return null;

            var user = new SignInUser();    // 共通ユーザクラス
            user.Id = creds.User;           // ユーザID
            user.jwt = new NSString(creds.IdentityToken, NSStringEncoding.UTF8).ToString(); // JWT Token

            return user;

        }

        //
        // awaitできるようにSignIn処理をクラス化する
        //
        class AuthManager : NSObject, IASAuthorizationControllerDelegate, IASAuthorizationControllerPresentationContextProviding
        {
            public Task<ASAuthorizationAppleIdCredential> Credentials
                => tcsCredential?.Task;

            TaskCompletionSource<ASAuthorizationAppleIdCredential> tcsCredential;

            UIWindow presentingAnchor;

            public AuthManager(UIWindow presentingWindow)
            {
                tcsCredential = new TaskCompletionSource<ASAuthorizationAppleIdCredential>();
                presentingAnchor = presentingWindow;
            }

            public UIWindow GetPresentationAnchor(ASAuthorizationController controller)
                => presentingAnchor;

            [Export("authorizationController:didCompleteWithAuthorization:")]
            public void DidComplete(ASAuthorizationController controller, ASAuthorization authorization)
            {
                var creds = authorization.GetCredential<ASAuthorizationAppleIdCredential>();
                tcsCredential?.TrySetResult(creds);
            }

            [Export("authorizationController:didCompleteWithError:")]
            public void DidComplete(ASAuthorizationController controller, NSError error)
                => tcsCredential?.TrySetException(new Exception(error.LocalizedDescription));
        }
    }
}

Sign In with Appleの実装詳細に関しては別の文献を参照してください。
(名前とメールアドレスは最初の1回目しか取得できないとか、色々と癖があります。。。)

サインインが成功するとASAuthorizationAppleIdCredentialが取得できます。
このクラスのIdentityTokenメンバーがOpenIDで使用するJWTトークンになりますので、それを使用して、AWSサービスにアクセスする際のCredentialオブジェクトを生成します。

  var credentials = new CognitoAWSCredentials(COGNITO_IDP_ID, RegionEndpoint.APNortheast1);
  credentials.AddLogin("appleid.apple.com", user.IdToken);

このCredentialをAWS SDKの各サービス用クライアント生成時に使用することで、AWSサービスに設定された権限でアクセスが可能になります。

注意事項

取得した認証トークンには有効期限があります。
Sign In with Appleで取得できるトークンの期限は10分で、この期限を超えて取得できたトークンを使用するとAWS SDKから、下記のExceptionが発生します。

Amazon.CognitoIdentity.Model.NotAuthorizedException

これをCatchしたら、再ログイン処理を行ってから再試行する、という実装が必要です。

この再ログイン処理ですが、Googole SignInでは、ユーザに再度サインイン画面を表示しなくても、内部でRefreshTokenによるAccess Tokenの再発行が行えます。(Silent SignIn)

しかし、Sign In with Appleでは、Refresh TokenによるAccess Tokenの再発行処理の口がありません。。。
(私が見つけられていないだけかも。。。)

今回私が開発した「LiveCapture3 Remote」では、10分後に再度サインイン画面を表示する、という流れでも特に問題なかったので、そのままにしていますが、もし、Sign In with AppleでのSilent SignInの方法があれば、どなたか教えてください。。

参考

GoogleアカウントでAWSサービスに接続する
Face IDでログイン!Sign in with AppleをiOSアプリに組み込む
signin with apple 実装するときの注意点まとめ

Yukio-Ichikawa
Daddy's Office代表 Windowsアプリケーション、クラウドアプリケーション、スマホアプリケーション開発を行っています。
https://lc3.daddysoffice.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした