Использование входа в Apple в Xamarin.Forms
Вход с помощью Apple предназначен для всех новых приложений в iOS 13, использующих сторонние службы проверки подлинности. Сведения о реализации между iOS и Android отличаются. В этом руководстве описывается, как это можно сделать сегодня Xamarin.Forms.
В этом руководстве и примере определенные службы платформы используются для обработки входа с помощью Apple:
- Android с помощью универсальной веб-службы, разговаривая с Функции Azure с OpenID/OpenAuth
- iOS использует собственный API для проверки подлинности в iOS 13 и возвращается к универсальной веб-службе для iOS 12 и ниже
Пример потока входа Apple
В этом примере представлена реализация единого входа Apple для работы в приложении Xamarin.Forms .
Мы используем два Функции Azure, чтобы помочь в потоке проверки подлинности:
applesignin_auth
— создает URL-адрес авторизации для входа Apple и перенаправляет его. Мы делаем это на стороне сервера вместо мобильного приложения, чтобы мы могли кэшироватьstate
и проверить его, когда серверы Apple отправляют обратный вызов.applesignin_callback
— обрабатывает обратный вызов POST из Apple и безопасно обменивается кодом авторизации для маркера доступа и маркера идентификатора. Наконец, он перенаправляет обратно в схему URI приложения, передавая маркеры в фрагменте URL-адреса.
Мобильное приложение регистрирует себя для обработки пользовательской схемы URI, выбранной (в данном случае xamarinformsapplesignin://
), чтобы applesignin_callback
функция могли ретрансляцию маркеров обратно в него.
Когда пользователь запускает проверку подлинности, выполняются следующие действия.
- Мобильное приложение создает и
state
значениеnonce
и передает их в функциюapplesignin_auth
Azure. - Функция
applesignin_auth
Azure создает URL-адрес авторизации для входа Apple (используя предоставленныйstate
и) иnonce
перенаправляет браузер мобильного приложения на него. - Пользователь безопасно вводит свои учетные данные на странице авторизации входа Apple, размещенной на серверах Apple.
- После завершения потока входа Apple на серверах Apple Apple перенаправляется на
redirect_uri
те, которые будут функциейapplesignin_callback
Azure. - Запрос от Apple, отправляемый
applesignin_callback
в функцию, проверяется, чтобы убедиться, что возвращается правильностьstate
, и что утверждения маркера идентификатора действительны. - Функция
applesignin_callback
Azure обмениваетсяcode
данными, размещенными в Apple, для маркера доступа, маркера обновления и маркера идентификатора (который содержит утверждения об идентификаторе пользователя, имени и электронной почте). - Функция
applesignin_callback
Azure, наконец, перенаправляется обратно в схему URI приложения (xamarinformsapplesignin://
) добавление фрагмента URI с маркерами (например.xamarinformsapplesignin://#access_token=...&refresh_token=...&id_token=...
- Мобильное приложение анализирует фрагмент URI в фрагмент
AppleAccount
и проверяетnonce
полученное утверждение соответствуетnonce
созданному в начале потока. - Мобильное приложение теперь проходит проверку подлинности!
Функции Azure
В этом примере используется Функции Azure. Кроме того, ASP.NET core Controller или аналогичное решение веб-сервера может обеспечить одинаковые функциональные возможности.
Настройка
При использовании Функции Azure необходимо настроить несколько параметров приложения:
APPLE_SIGNIN_KEY_ID
- Это вашKeyId
из более ранних версий.APPLE_SIGNIN_TEAM_ID
— Обычно это идентификатор группы, найденный в профиле членства.APPLE_SIGNIN_SERVER_ID
: это из более ранних версийServerId
. Это не идентификатор пакета приложений, а идентификатор созданного идентификатора служб.APPLE_SIGNIN_APP_CALLBACK_URI
— Это настраиваемая схема URI, с которой вы хотите вернуться в приложение. В этом примереxamarinformsapplesignin://
используется.APPLE_SIGNIN_REDIRECT_URI
— URL-адрес перенаправления, который вы настраиваете при создании идентификатора служб в разделе "Конфигурация входа Apple". Чтобы проверить, это может выглядеть примерно так:http://local.test:7071/api/applesignin_callback
APPLE_SIGNIN_P8_KEY
— Текстовое содержимое.p8
файла со всеми\n
новыми строками удалено, поэтому это одна длинная строка.
Вопросы безопасности
Никогда не храните ключ P8 в коде приложения. Код приложения легко скачать и дизассемблировать.
Также считается плохой практикой размещения WebView
потока проверки подлинности и перехвата событий навигации ПО URL-адреса для получения кода авторизации. В настоящее время в настоящее время нет полностью безопасного способа обработки входа с помощью Apple на устройствах, отличных от iOS13+ без размещения кода на сервере для обработки обмена токенами. Мы рекомендуем разместить код создания URL-адреса авторизации на сервере, чтобы кэшировать состояние и проверить его, когда Apple выдает обратный вызов POST на сервер.
Кроссплатформенная служба входа
Xamarin.Forms С помощью DependencyService можно создать отдельные службы проверки подлинности, использующие службы платформы в iOS, а также универсальную веб-службу для Android и других платформ, отличных от iOS, на основе общего интерфейса.
public interface IAppleSignInService
{
bool Callback(string url);
Task<AppleAccount> SignInAsync();
}
В iOS используются собственные API:
public class AppleSignInServiceiOS : IAppleSignInService
{
#if __IOS__13
AuthManager authManager;
#endif
bool Is13 => UIDevice.CurrentDevice.CheckSystemVersion(13, 0);
WebAppleSignInService webSignInService;
public AppleSignInServiceiOS()
{
if (!Is13)
webSignInService = new WebAppleSignInService();
}
public async Task<AppleAccount> SignInAsync()
{
// Fallback to web for older iOS versions
if (!Is13)
return await webSignInService.SignInAsync();
AppleAccount appleAccount = default;
#if __IOS__13
var provider = new ASAuthorizationAppleIdProvider();
var req = provider.CreateRequest();
authManager = new AuthManager(UIApplication.SharedApplication.KeyWindow);
req.RequestedScopes = new[] { ASAuthorizationScope.FullName, ASAuthorizationScope.Email };
var controller = new ASAuthorizationController(new[] { req });
controller.Delegate = authManager;
controller.PresentationContextProvider = authManager;
controller.PerformRequests();
var creds = await authManager.Credentials;
if (creds == null)
return null;
appleAccount = new AppleAccount();
appleAccount.IdToken = JwtToken.Decode(new NSString(creds.IdentityToken, NSStringEncoding.UTF8).ToString());
appleAccount.Email = creds.Email;
appleAccount.UserId = creds.User;
appleAccount.Name = NSPersonNameComponentsFormatter.GetLocalizedString(creds.FullName, NSPersonNameComponentsFormatterStyle.Default, NSPersonNameComponentsFormatterOptions.Phonetic);
appleAccount.RealUserStatus = creds.RealUserStatus.ToString();
#endif
return appleAccount;
}
public bool Callback(string url) => true;
}
#if __IOS__13
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));
}
#endif
Флаг __IOS__13
компиляции используется для поддержки iOS 13, а также устаревших версий, которые падают в универсальную веб-службу.
В Android используется универсальная веб-служба с Функции Azure:
public class WebAppleSignInService : IAppleSignInService
{
// IMPORTANT: This is what you register each native platform's url handler to be
public const string CallbackUriScheme = "xamarinformsapplesignin";
public const string InitialAuthUrl = "http://local.test:7071/api/applesignin_auth";
string currentState;
string currentNonce;
TaskCompletionSource<AppleAccount> tcsAccount = null;
public bool Callback(string url)
{
// Only handle the url with our callback uri scheme
if (!url.StartsWith(CallbackUriScheme + "://"))
return false;
// Ensure we have a task waiting
if (tcsAccount != null && !tcsAccount.Task.IsCompleted)
{
try
{
// Parse the account from the url the app opened with
var account = AppleAccount.FromUrl(url);
// IMPORTANT: Validate the nonce returned is the same as our originating request!!
if (!account.IdToken.Nonce.Equals(currentNonce))
tcsAccount.TrySetException(new InvalidOperationException("Invalid or non-matching nonce returned"));
// Set our account result
tcsAccount.TrySetResult(account);
}
catch (Exception ex)
{
tcsAccount.TrySetException(ex);
}
}
tcsAccount.TrySetResult(null);
return false;
}
public async Task<AppleAccount> SignInAsync()
{
tcsAccount = new TaskCompletionSource<AppleAccount>();
// Generate state and nonce which the server will use to initial the auth
// with Apple. The nonce should flow all the way back to us when our function
// redirects to our app
currentState = Util.GenerateState();
currentNonce = Util.GenerateNonce();
// Start the auth request on our function (which will redirect to apple)
// inside a browser (either SFSafariViewController, Chrome Custom Tabs, or native browser)
await Xamarin.Essentials.Browser.OpenAsync($"{InitialAuthUrl}?&state={currentState}&nonce={currentNonce}",
Xamarin.Essentials.BrowserLaunchMode.SystemPreferred);
return await tcsAccount.Task;
}
}
Итоги
В этой статье описаны действия, необходимые для настройки входа в Apple для использования в приложениях Xamarin.Forms .