Použití přihlášení s Apple in Xamarin.Forms
Přihlášení pomocí Apple je určené pro všechny nové aplikace v iOSu 13, které používají ověřovací služby třetích stran. Podrobnosti implementace mezi iOSem a Androidem jsou poměrně odlišné. Tato příručka vás provede tím, jak to můžete udělat dnes v Xamarin.Forms.
V této příručce a ukázce se ke zpracování přihlášení pomocí Apple používají konkrétní služby platformy:
- Android s využitím obecné webové služby, která komunikuje se službou Azure Functions s OpenID/OpenAuth
- iOS používá nativní rozhraní API pro ověřování v iOSu 13 a vrátí se do obecné webové služby pro iOS 12 a níže.
Ukázkový tok přihlášení Apple
Tato ukázka nabízí názornou implementaci pro získání apple přihlášení, aby fungovalo ve vaší Xamarin.Forms aplikaci.
K pomoci s tokem ověřování používáme dvě funkce Azure Functions:
applesignin_auth
- Vygeneruje autorizační adresu URL přihlášení Apple a přesměruje na ni. Děláme to na straně serveru místo mobilní aplikace, abychom je mohli uložit do mezipamětistate
a ověřit, když servery Apple posílají zpětné volání.applesignin_callback
- Zpracovává zpětné volání POST od Společnosti Apple a bezpečně vyměňuje autorizační kód pro přístupový token a token ID. Nakonec se přesměruje zpět do schématu identifikátoru URI aplikace a předá tokeny v fragmentu adresy URL.
Mobilní aplikace se zaregistruje pro zpracování vlastního schématu identifikátorů URI, které jsme vybrali (v tomto případě xamarinformsapplesignin://
), aby applesignin_callback
funkce mohl tokeny předávat zpět do ní.
Když uživatel spustí ověřování, dojde k následujícím krokům:
- Mobilní aplikace vygeneruje
nonce
hodnotu astate
předá je doapplesignin_auth
funkce Azure. - Funkce
applesignin_auth
Azure vygeneruje autorizační adresu URL apple pro přihlášení (pomocí zadanéstate
adresy anonce
) a přesměruje na ni prohlížeč mobilních aplikací. - Uživatel bezpečně zadá přihlašovací údaje na autorizační stránce Apple Sign In hostované na serverech Společnosti Apple.
- Jakmile se tok Přihlášení Apple dokončí na serverech Společnosti Apple, Apple Přesměruje na
redirect_uri
applesignin_callback
funkci Azure. - Požadavek od společnosti Apple odeslaný do
applesignin_callback
funkce se ověří, aby se zajistilo, že je vrácena správnástate
odpověď a že deklarace identity tokenu ID jsou platné. - Funkce
applesignin_callback
Azure vyměnícode
odesílané společností Apple za přístupový token, obnovovací token a token ID (který obsahuje deklarace identity týkající se ID uživatele, jména a e-mailu). - Funkce
applesignin_callback
Azure nakonec přesměruje zpět do schématu identifikátoru URI aplikace (xamarinformsapplesignin://
) připojující fragment identifikátoru URI s tokeny (např.xamarinformsapplesignin://#access_token=...&refresh_token=...&id_token=...
). - Mobilní aplikace parsuje fragment identifikátoru URI a
AppleAccount
ověří, ženonce
přijatá deklarace identity odpovídánonce
vygenerovanému na začátku toku. - Mobilní aplikace je teď ověřená.
Azure Functions
Tato ukázka používá Azure Functions. Alternativně může řešení ASP.NET Core Controller nebo podobné webového serveru poskytovat stejné funkce.
Konfigurace
Při používání Azure Functions je potřeba nakonfigurovat několik nastavení aplikace:
APPLE_SIGNIN_KEY_ID
- Tohle je tvojeKeyId
dřívější.APPLE_SIGNIN_TEAM_ID
- Obvykle se jedná o ID vašeho týmu, které najdete ve vašem profilu členství.APPLE_SIGNIN_SERVER_ID
: Jedná se oServerId
předchozí verzi. Nejedná se o ID sady prostředků aplikace, ale o identifikátor ID služeb, které jste vytvořili.APPLE_SIGNIN_APP_CALLBACK_URI
– Jedná se o vlastní schéma identifikátorů URI, pomocí kterého chcete přesměrovat zpět do aplikace. V této ukázcexamarinformsapplesignin://
se používá.APPLE_SIGNIN_REDIRECT_URI
– Adresa URL pro přesměrování, kterou nastavíte při vytváření ID služeb v části Konfigurace přihlášení Apple. Pokud chcete testovat, může to vypadat nějak takto:http://local.test:7071/api/applesignin_callback
APPLE_SIGNIN_P8_KEY
– Textový obsah.p8
souboru se všemi odstraněnými novými\n
spojnicemi, takže je to jeden dlouhý řetězec.
Bezpečnostní aspekty
Nikdy neukládejte klíč P8 uvnitř kódu aplikace. Kód aplikace se snadno stáhne a rozebere.
Také se považuje za špatný postup použití WebView
k hostování ověřovacího toku a k zachycení událostí navigace adresy URL za účelem získání autorizačního kódu. V současné době neexistuje žádný plně zabezpečený způsob, jak zpracovat přihlášení s Apple na zařízeních bez iOS13+ bez hostování kódu na serveru pro zpracování výměny tokenů. Doporučujeme hostovat kód pro generování autorizační adresy URL na serveru, abyste mohli uložit stav do mezipaměti a ověřit ho, když Apple vydá zpětné volání POST na váš server.
Multiplatformní přihlašovací služba
Xamarin.Forms Pomocí DependencyService můžete vytvořit samostatné ověřovací služby, které používají služby platformy v iOSu, a obecnou webovou službu pro Android a další platformy bez iOS založené na sdíleném rozhraní.
public interface IAppleSignInService
{
bool Callback(string url);
Task<AppleAccount> SignInAsync();
}
V iOSu se používají nativní rozhraní 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
Příznak __IOS__13
kompilace slouží k poskytování podpory pro iOS 13 i starších verzí, které se přecházely do obecné webové služby.
V Androidu se používá obecná webová služba se službou Azure Functions:
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;
}
}
Shrnutí
Tento článek popisuje kroky potřebné k nastavení přihlášení pomocí Apple pro použití ve vašich Xamarin.Forms aplikacích.