[iOS13] ASWebAuthenticationSession用いたOAuth認証の変更点


はじめに

OAuth認証やASWebAuthenticationSession、などについてはここではふれません。
ASWebAuthenticationSession用いたOAuth認証をiOS12で実装しており、これからiOS13に対応する方法の一例を書きたいと思います。

UIApplication.sharedApplication.keyWindow; が iOS13 で DEPRECATED になってしまったので、別の方法に書き換えました。

変更点

  • ASWebAuthenticationSessionstart を呼び出す前に、 presentationContextProvider を設定しなければいけない

In macOS, and for iOS apps with a deployment target of iOS 13 or later, after you call start, the session instance stores a strong reference to itself. To avoid deallocation during the authentication process, the session keeps the reference until after it calls the completion handler.

For iOS apps with a deployment target earlier than iOS 13, your app must keep a strong reference to the session to prevent the system from deallocating the session while waiting for authentication to complete.

参照元

これにより、以下の書き換えが必要になります。

iOS12.m
self.webAuthenticationSession = [[ASWebAuthenticationSession alloc] initWithURL:[NSURL URLWithString:url] callbackURLScheme:nil completionHandler:^(NSURL *callbackURL, NSError *error) {
    // SessionToken の取得など
    self.webAuthenticationSession = nil;
}];
[self.webAuthenticationSession start];

iOS13.m
self.webAuthenticationSession = [[ASWebAuthenticationSession alloc] initWithURL:[NSURL URLWithString:url] callbackURLScheme:nil completionHandler:^(NSURL *callbackURL, NSError *error) {
    // SessionToken の取得など
    self.webAuthenticationSession = nil;
}];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *)) {
    self.webAuthenticationSession.presentationContextProvider = uiViewController; // uiViewControllerについては後で記載
}
#endif
[self.webAuthenticationSession start];

以下のように presentationContextProviderASWebAuthenticationPresentationContextProviding Protocol を実装したインスタンスを設定すればいいだけです。

self.webAuthenticationSession.presentationContextProvider = uiViewController;

例えば

HogeLoginViewController.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

API_AVAILABLE(ios(13.0), macos(10.15))
@interface HogeLoginViewController : UIViewController <ASWebAuthenticationPresentationContextProviding>

@end
#endif
HogeLoginViewController.m
#import "HogeLoginViewController.h"

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

@implementation HogeLoginViewController {

}

...

- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
    return view.window;
}
...

@end

#endif

問題点

これだと、認証システム基盤を作成するには、問題があります。
APIの引数にUI要素のものを渡し他場合、以下のような問題が出てくるからです。

  • Androidと共通基盤の場合、引数が異なる
  • 引数にUI要素なものを渡さないといけない?
  • Unityなどで利用する場合、どうするの?

解決策は簡単で、 UIViewController を使わないだけです。

解決策

HogeBridgeAPI.h
#import <Foundation/Foundation.h>

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

API_AVAILABLE(ios(13.0), macos(10.15))
@interface HogeBridgeAPI : NSObject <ASWebAuthenticationPresentationContextProviding>

@end
#endif
HogeBridgeAPI.m
#import "HogeBridgeAPI.h"

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000

@implementation HogeBridgeAPI {

}

- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
    for (UIScene *uiScene in UIApplication.sharedApplication.connectedScenes) {
        if (uiScene.activationState != UISceneActivationStateForegroundActive) {
            continue;
        }
        if ([uiScene isKindOfClass:UIWindowScene.class]) {
            UIWindowScene *uiWindowScene = (UIWindowScene *)uiScene;
            for (UIWindow *uiWindow in uiWindowScene.windows) {
                if(uiWindow.isKeyWindow){
                    return uiWindow;
                }
            }
        }
    }
    return nil;
}

@end
#endif

HogeBridgeAPIなるものを作成します。

for (UIScene *uiScene in UIApplication.sharedApplication.connectedScenes) {
    if (uiScene.activationState != UISceneActivationStateForegroundActive) {
        continue;
    }
    if ([uiScene isKindOfClass:UIWindowScene.class]) {
        UIWindowScene *uiWindowScene = (UIWindowScene *)uiScene;
        for (UIWindow *uiWindow in uiWindowScene.windows) {
            if(uiWindow.isKeyWindow){
                return uiWindow;
            }
        }
    }
}

presentationAnchorForWebAuthenticationSession の戻り値は、上記とすることで UIViewController を使わくても大丈夫です。

あとは、 コンストラクタなどで HogeBridgeAPI を生成

self.webAuthenticationSession.presentationContextProvider = self.bridgeAPI;

などしてあげれば解決します。