Sind Sie ein .NET-Entwickler, der schon immer eine mobile Anwendung erstellen wollte? Oder haben Sie vielleicht versucht, native mobile Anwendungen mit Android oder iOS zu erstellen, aber die Sprachen haben Ihnen nicht gefallen? Nun, dann haben Sie Glück! Die .NET-Welt ist mit Xamarin gesegnet, einer Reihe von Tools, mit denen Sie in Visual Studio mobile Anwendungen für Android, iOS und Windows erstellen können.

Xamarin hat zwei Hauptvarianten: Xamarin Plattform (Xamarin.iOS und Xamarin.Android) und Xamarin.Forms. Mit Xamarin.Forms kann ein Großteil der Geschäftslogik und der Benutzeroberfläche in einem gemeinsamen Projekt geschrieben werden, das voll funktionsfähige Apps auf allen drei Betriebssystemen iOS, Android und Windows (UWP) erzeugt. Die Xamarin-Plattform hingegen ist sehr plattformspezifisch und ähnelt eher dem Schreiben nativer Apps, allerdings mit C#.

In diesem Tutorial werde ich die Xamarin-Plattform und das Android-Betriebssystem-Toolset Xamarin.Android näher betrachten. Ziel ist es, eine einfache native Android-App mit grundlegender Benutzerauthentifizierung zu erstellen.

Visual Studio und Ihre Umgebung einrichten

Um dem Tutorial folgen zu können, benötigen Sie eine Kopie von Visual Studio sowie die Arbeitslast „Mobile Entwicklung mit .NET“. Sie können diese Funktion entweder bei der Erstinstallation von Visual Studio aktivieren oder über den Menüpunkt ‚Tools -> Get Tools and Features…‘ darauf zugreifen:

Beim Testen und Ausführen Ihrer App haben Sie die Wahl zwischen einem Android-Emulator, der auf Ihrem Entwicklungsrechner läuft, oder der direkten Verbindung mit einem vorhandenen Android-Gerät. Hier gibt es keine richtige Option, und verschiedene Entwickler bevorzugen unterschiedliche Formfaktoren. Wenn Sie sich für die erste Option entscheiden, müssen Sie nach der Auswahl des Workloads sicherstellen, dass auf der rechten Seite („Installationsdetails“) die Kontrollkästchen für Intel Hardware Accelerated Execution Manager und Google Android Emulator aktiviert sind (siehe oben).

Überprüfen Sie Ihre Android-Umgebung in Visual Studio

Um zu überprüfen, ob alles richtig installiert und konfiguriert wurde, gehen Sie zu ‚Tools -> Options -> Xamarin -> Android Settings‘ und überprüfen Sie, ob die Pfade für Java Development Kit Location und Android SDK Location gültig sind (d. h. ein grünes Häkchen haben).

Wenn einer der beiden Pfade fehlt, müssen Sie das Java Development Kit bzw. das Android SDK manuell installieren.

Erstellen Sie eine Xamarin App

Starten Sie, indem Sie ein neues Projekt erstellen und wählen Sie die Master-Vorlage „Android App (Xamarin)“ (zu finden im Android-Menü). Auf der nächsten Seite sollten Sie die Option „Single View App“ auswählen, da dies eine hervorragende Einstiegsvorlage für die Arbeit ist.

In Bezug auf die minimale Android-Version ist dies eine persönliche Entscheidung des Entwicklers. Es gibt hier einen Kompromiss zwischen der Möglichkeit, auf die neuesten und besten API-Funktionen in neueren Versionen zuzugreifen, und der Unterstützung Ihrer Kunden, die ältere Versionen haben. Um Ihnen bei dieser Entscheidung zu helfen, veröffentlicht Google in regelmäßigen Abständen die Daten zur Verteilung der Plattformversionen, die es im Rahmen seines Distribution Dashboards sammelt. Ich persönlich bevorzuge die Version 5.0 oder 6.0, je nachdem, ob es sich um eine App für die Öffentlichkeit oder um eine Auftragsanwendung nur für Unternehmenstelefone handelt (d. h., dass diese wahrscheinlich über die neuesten Updates verfügen); in diesem Beispiel habe ich mich für die letztere Version entschieden. Beachten Sie, dass sich diese Version von der Ziel-Android-Version unterscheidet, die immer auf die neueste SDK-Version eingestellt werden sollte, da alles darunter liegende nicht im Google Play Store akzeptiert wird.

Nach der Erstellung der App müssen Sie nur noch die erforderlichen NuGet-Pakete importieren. Für dieses Tutorial benötigen Sie:

  • Xamarin.OpenId.AppAuth.Android – Für die Benutzerauthentifizierung wird der OpenID Connect Standard (eine Erweiterung von OAuth 2.0) verwendet. Der einfachste Weg, Client-Code zu implementieren, der sich an diese Spezifikation hält, ist die Verwendung des AppAuth-Client-SDK für Android, und hilfreicherweise hat Xamarin ein Paket dieser Funktionalität portiert, das Sie verwenden können.
  • System.IdentityModel.Tokens.Jwt – Die Methode der Authentifizierung verwendet hier JSON-Web-Token. Um die benötigten Daten aus diesen Token zu extrahieren, benötigen Sie dieses Paket.

Zusätzlich benötigen Sie diese beiden Pakete, da sie von AppAuth benötigt werden:

  • Xamarin.Android.Support.v4
  • Xamarin.Android.Support.CustomTabs

Machen Sie sich mit dem Xamarin-Projekt vertraut

Wenn Sie noch nie mit Android gearbeitet haben, ist eines der wichtigsten Prinzipien, mit dem Sie sich vertraut machen müssen, das Konzept einer Activity. Activities sind Komponenten, die zur Anzeige der Benutzeroberfläche verwendet werden. In ihrer einfachsten Form können Sie sich Activities als Seiten in einer App vorstellen, zwischen denen der Benutzer navigieren kann. Eine Activity wird im Code durch eine Klasse dargestellt, aber wie eine Seite in ASP.NET können (und werden fast immer) Sie eine XML-basierte Layout-Datei (eine .axml-Datei) mit ihr verknüpfen, um ein anzeigbares Design zu erhalten. Alle neuen Projekte erstellen eine „MainActivity.cs“- und „activity_main.axml“-Datei, die beim Öffnen der Anwendung als erste Aktivität (d. h. Seite) ausgeführt wird. Dies kann durch Verwendung der Eigenschaft MainLauncher = true im Activity-Attribut der Klasse in eine beliebige andere Aktivität geändert werden.

Ressourcen sind so konzipiert, dass sie in einem eigenen Verzeichnis behandelt werden und einer etwas strengen Namenskonvention folgen. Ich würde dringend empfehlen, so viele Ressourcen wie möglich in diesem Verzeichnis zu speichern, da dies die Wiederverwendung dieser Variablen für die weitere Entwicklung vereinfacht. Im Verzeichnis „values“ des Verzeichnisses „Resources“ finden Sie Dateien mit bestimmten Zwecken:

  • Strings.xml – Enthält alle benutzerorientierten Strings. Diese Datei ist besonders wichtig, da sie es Ihnen ermöglicht, Ihre Strings für ein globales Publikum zu lokalisieren.
  • Styles.xml – Hier finden Sie die Attribute für die Gestaltung Ihrer Designobjekte; stellen Sie sich diese Datei wie eine CSS-Datei vor.
  • Colors.xml – Ein Ort, an dem Sie Verweise auf die Farben speichern, die Sie am häufigsten als Teil Ihrer Gestaltung verwenden.
  • Dimens.xml – Wie der Name schon sagt, definieren Sie hier die Abmessungen für das Layout Ihrer Anwendung.

Die anderen wichtigen Verzeichnisse folgen keiner Namenskonvention für ihre Dateien, aber sie müssen bestimmte Dateitypen enthalten:

  • Layout – Speicherort für Ihre .axml-Dateien. Diese Dateien definieren vollständige Aktivitäts-Layouts, Layout-Komponenten, die Sie programmatisch generieren und auffüllen, Layouts von Dialogfeldern (Warnmeldungen) usw.
  • Menu – Hier finden Sie Definitionen von Menüs und ihren Elementen. Es handelt sich um .xml-Dateien mit menu als Stammelement und item untergeordneten Elementen, die mit group-Elementen gruppiert werden können. Die gebräuchlichsten Menüs sind das Überlaufmenü (von der Schaltfläche mit den drei vertikalen Punkten) oder das Navigationsmenü (von der Hamburger-Schaltfläche auf der Startseite).
  • Mipmap – Hier werden Bilder definiert, die je nach Bildschirmdichte skaliert werden müssen, d.h. die von anderen Anwendungen referenziert und nicht intern verwendet werden. Das Icon der Anwendung ist der häufigste Inhalt, den Sie in den mipmap-Verzeichnissen ablegen würden.
  • Drawable – Nicht standardmäßig in einer Standard-Projektvorlage enthalten, kann aber selbst erstellt werden. Hier speichern Sie Ihre Bilder/zeichnbaren Inhalte, die in der Anwendung verwendet werden sollen, z.B. Splash-Screen, benutzerdefinierte Fortschrittsbalken, Icons, die in der Anwendung verwendet werden, usw.

Zuletzt, wenn Sie irgendwelche Rohinhaltsdateien haben, die Sie als Teil Ihrer Anwendung verwenden wollen (z.B. eine Text- oder Schriftartdatei), dann ist das Assets-Verzeichnis der Ort, an dem Sie sie ablegen. Als Assets werden diese Dateien mit Ihrer Anwendung bereitgestellt, damit Sie mit dem Asset Manager darauf zugreifen können.

Um mehr über Assets und Ressourcen und deren Verwendung zu erfahren, gibt es in jedem Verzeichnis eines neu erstellten Projekts praktische „About“-Textdateien.

Hinzufügen einer Benutzerauthentifizierung zu Xamarin mit OpenID Connect

Die meisten Anwendungen erfordern heutzutage eine Form der Benutzeridentifikation, damit der Entwickler maßgeschneiderte Erfahrungen anbieten kann und der Benutzer seine Daten über Geräte/Installationen hinweg behalten kann. Hinzu kommt die Frage der Zugriffskontrolle, die nützlich sein kann, um eine Untergruppe von Benutzern für zusätzliche Funktionen zu autorisieren. Natürlich kann dies eine ziemlich mühsame Aufgabe sein, vor allem, wenn Sie Apps erstellen und für jede einzelne ein neues System benötigen. Zum Glück können Sie mit Okta eine Anwendung in wenigen Minuten einrichten, und dann ist die ganze harte Arbeit für Sie erledigt! Der Okta-Dienst ist OAuth 2.0-konform und ein zertifizierter OpenID Connect Provider und arbeitet daher perfekt mit dem AppAuth-Client-SDK für alle Ihre Authentifizierungs- und Autorisierungsanforderungen zusammen.

Einrichten Ihrer Okta-Anwendung

Zunächst sollten Sie eine neue Anwendung in Ihrem Okta-Konto für dieses Projekt einrichten. Wenn Sie noch keines haben, ist es ganz einfach, ein neues, kostenloses Entwicklerkonto zu erstellen.

Wenn das erledigt ist und Sie sich im Entwickler-Dashboard angemeldet haben, notieren Sie sich die Org-URL, da wir diese später benötigen:

Aus dem Dashboard gehen Sie auf die Registerkarte „Anwendungen“ und von dort auf „Anwendung hinzufügen“. Da Sie eine native Android-Anwendung erstellen, wählen Sie am besten die Plattformvorlage „Native“.

Fügen Sie auf dieser Seite einen Namen für Ihre Anwendung hinzu und lassen Sie alles andere als Standard. Notieren Sie sich nach dem Speichern Ihre Login-Redirect-URIs und Ihre Client-ID, da Sie diese als Nächstes benötigen werden.

So können Sie diese drei Werte von Okta mit Leichtigkeit in der Zukunft verwenden, würde ich empfehlen, sie in ihre eigene statische Klasse innerhalb eines neuen Verzeichnisses für die Authentifizierungslogik zu setzen:

public static class Configuration{ public const string ClientId = "{yourClientId}"; public const string LoginRedirectUri = "{yourRedirectUri}"; public const string OrgUrl = "https://{yourOktaDomain}";}

Erstellen Sie den Authentifizierungsprovider

Lassen Sie uns den Boilerplate-Code aus dem Weg schaffen. Sie müssen die Anwendung so konfigurieren, dass sie über Ihr Redirect-URI-Schema informiert ist. Das Schema ist Ihr Login-Redirect-URI (in Form des Standard-Reverse-Domain-Namens) ohne den Pfad. Im obigen Screenshot wäre mein Schema beispielsweise „com.oktapreview.dev-123456“.

Am einfachsten ist es, wenn Sie den folgenden Vorsatzfilterausschnitt in Ihre AndroidManifest.xml-Datei im Properties-Ordner Ihrer Lösung einfügen. Fügen Sie das folgende XML innerhalb des Application-Tags ein und ändern Sie den Schemawert in Ihren eigenen:

<activity android:name="net.openid.appauth.RedirectUriReceiverActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="{yourRedirectRriScheme}" /> </intent-filter></activity>

Sie müssen auch das Modell für das Ergebnis Ihrer Autorisierung mit einer einfachen Klasse definieren. Ich werde zwar nicht alle Werte, die ich unten geschrieben habe, im Code verwenden, aber ich werde zeigen, wie man sie auffüllt, damit Sie sie später verwenden können. Da dies ein Teil des Modells Ihrer Anwendung ist, erstellen Sie einen Ordner mit dem Namen Models und fügen Sie darin eine AuthorizationResult.cs-Klasse ein. Fügen Sie dann den folgenden Code hinzu:

public class AuthorizationResult{ public string AccessToken { get; set; } public string IdentityToken { get; set; } public bool IsAuthorized { get; set; } public string RefreshToken { get; set; } public string Scope { get; set; }}

Android verfügt nicht über einen globalen Status, mit dem Sie arbeiten können. Wenn Sie also einfache Werte zwischen Aktivitäten übergeben möchten, ist der beste Weg, dies mit der Extras-Funktionalität des Intent-Objekts zu tun. Ein Intent ist eine vordefinierte Klasse in Android und ein weiteres grundlegendes Konzept, das man verstehen muss. Es ist die Abstraktion eines auszuführenden Vorgangs (d. h. Ihrer „Absichten“), und um zu einer anderen Aktivität zu navigieren, müssen Sie eine Instanz dieser Klasse mit der Aktivität erstellen, zu der Sie „gehen wollen“. Die Extras-Eigenschaften des Intent-Objekts sind im Grunde nur ein Wörterbuch von Schlüsseln zu Objektwerten und werden durch Put– und typisierte Get-Methoden angesprochen.

Während diese Methoden die Verwendung relativ klar und einfach halten, möchte ich persönlich den gesamten Zugriff auf sie innerhalb ihrer eigenen Klasse (genauer gesagt, einer Erweiterungsklasse) halten, um eine bessere Trennung der Belange zu gewährleisten. Dies ist äußerst nützlich, da Sie nicht klassenübergreifend auf die Schlüssel zugreifen müssen und Typsicherheit beim Setzen und Abrufen dieser Werte gewährleisten können. In Ihrem Berechtigungsprovider möchten Sie AuthState speichern, überprüfen können, ob es vorhanden ist, und es zurückgeben, wenn es vorhanden ist. Erstellen Sie einen neuen Ordner namens Extensions im Stammverzeichnis der Lösung. Füge dann eine neue Klasse namens IntentExtensions.cs hinzu. Machen Sie die Klasse public und static und fügen Sie dann den folgenden Code innerhalb der Klasse hinzu:

public const string AuthStateKey = "authState";public static string GetAuthStateExtra(this Intent intent){ return intent.GetStringExtra(AuthStateKey);}public static bool HasAuthStateExtra(this Intent intent){ return intent.HasExtra(AuthStateKey);}public static void PutAuthStateExtra(this Intent intent, AuthState authState){ intent.PutExtra(AuthStateKey, authState.JsonSerializeString());}public static bool TryGetAuthStateFromExtra(this Intent intent, out AuthState authState){ authState = null; if (!intent.HasAuthStateExtra()) { return false; } try { authState = AuthState.JsonDeserialize(intent.GetAuthStateExtra()); } catch (JSONException) { return false; } return authState != null;}

Jetzt ist es an der Zeit, den Autorisierungsanbieter AuthorizationProvider.cs im Ordner Authentication zu definieren, den Sie zuvor für die Klasse Configuration.cs erstellt haben. Zuerst entfernen Sie alle using Anweisungen innerhalb der neu erstellten Klasse, dann deklarieren Sie die Konfiguration als static readonly Variablen:

private static readonly Uri ConfigurationEndpoint = Uri.Parse($"{Configuration.OrgUrl}/oauth2/default/.well-known/openid-configuration");private static readonly string Scopes = new { "openid", "profile", "email", "offline_access" };

Der Konfigurationsendpunkt ist ein Standard in OpenID als der Discovery Endpunkt, um alles zu finden, was unterstützt wird. Beachten Sie, dass ich hier den „Standard“-Provider-Namen verwendet habe. Wenn Sie einen anderen Anbieter haben, müssen Sie dies hier ändern. Beachten Sie auch, dass hier die Android.Net-Variante der Uri-Klasse verwendet wird und nicht die System-Version – Sie müssen die erstere zu Ihren Verwendungen hinzufügen oder den Typ vollständig qualifizieren, damit dies funktioniert. Die Scopes Variable, wie jedes andere OpenID System, definiert was wir berechtigt sind zuzugreifen.

Als nächstes solltest du deine Mitgliedsvariablen deklarieren:

private AuthorizationRequest authorizationRequest;private PendingIntent completedIntent;private AuthState authorizationState;private readonly AuthorizationService authorizationService;private readonly Context context;private readonly TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();

Eine kurze Erklärung zu jeder:

  • Die Autorisierungsanfrage und die abgeschlossene Absicht sind Parameter die für die Verwendung beim Autorisierungsaufruf erstellt werden. Ich habe sie hier als globale Variablen geschrieben, um die Übergabe von Parametern an verschiedene Methoden zu minimieren.
  • Die authorizationState-Variable, wie sie genannt wird, definiert den aktuell gegebenen Autorisierungsstatus.
  • Die authorizationService-Variable enthält eine Instanz des Autorisierungsdienstes.
  • Die Kontextvariable hier ist die der aufrufenden Aktivität, so dass Sie sie bei Bedarf referenzieren können.
  • Die taskCompletionSource schließlich ermöglicht es Ihnen, all diese Aufrufe asynchron durchzuführen und nach Abschluss zurückzukehren.

Nun sollten Sie die Werte dieser schreibgeschützten Variablen in Ihrem Konstruktor definieren und die öffentlichen Methoden deklarieren, die Ihr Code aufrufen wird:

public AuthorizationProvider(Context context){ authorizationService = new AuthorizationService(context); this.context = context;}public async Task<AuthorizationResult> SignInAsync(){ ...}public void NotifyCallback(Intent intent){ ...}

Die SignInAsync-Methode ist, wie Sie vielleicht schon vermutet haben, eine asynchrone Methode zur Anmeldung eines Benutzers. Sie gibt die AuthorizationResult Klasse zurück, die Sie zuvor geschrieben haben. NotifyCallback hingegen dient dazu, dass die aufrufende Aktivität, sobald sie von der externen Anmeldeseite zurückgekehrt ist, den Autorisierungsanbieter zurückruft und ihm mitteilt, dass sie fertig ist. Die Anmeldemethode habe ich in mehrere Unterroutinen aufgeteilt und sieht wie folgt aus:

AuthorizationServiceConfiguration serviceConfiguration = await AuthorizationServiceConfiguration.FetchFromUrlAsync(ConfigurationEndpoint);BuildAuthorizationRequest(serviceConfiguration);BuildCompletedIntent(serviceConfiguration);return await RequestAuthorization();

Damit haben Sie die Dienstkonfiguration definiert, die Autorisierungsanforderung und die Absicht zum Aufruf nach Abschluss der Autorisierung erstellt und dann die Autorisierungsanforderung abgewartet. Die Autorisierungsanfrage wird wie folgt erstellt:

private void BuildAuthorizationRequest(AuthorizationServiceConfiguration serviceConfiguration){ AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder( serviceConfiguration, Configuration.ClientId, ResponseTypeValues.Code, Uri.Parse(Configuration.LoginRedirectUri)); builder.SetScope(string.Join(" ", Scopes)); authorizationRequest = builder.Build();}

Die Aufgabe dieser Methode ist es, die AuthorizationRequest.Builder Arbeit zu abstrahieren und die Anfrage zu erstellen. Als nächstes müssen Sie die Intent erstellen, sobald die Operation abgeschlossen ist:

private void BuildCompletedIntent(AuthorizationServiceConfiguration serviceConfiguration){ Intent intent = new Intent(context, typeof(MainActivity)); intent.PutAuthStateExtra(new AuthState()); completedIntent = PendingIntent.GetActivity(context, authorizationRequest.GetHashCode(), intent, 0);}

Die „Absicht“, die Sie hier ausführen möchten, ist die Rückkehr zu Ihrem MainActivity mit einem neuen AuthState. Der letzte Punkt in diesem Fluss ist die Ausführung der Anfrage:

private async Task<AuthorizationResult> RequestAuthorization(){ authorizationService.PerformAuthorizationRequest(authorizationRequest, completedIntent); await taskCompletionSource.Task; return new AuthorizationResult() { AccessToken = authorizationState?.AccessToken, IdentityToken = authorizationState?.IdToken, IsAuthorized = authorizationState?.IsAuthorized ?? false, RefreshToken = authorizationState?.RefreshToken, Scope = authorizationState?.Scope };}

Da PerformAuthorizationRequest synchron ist und leer zurückgibt, wartet der Code auf das taskCompletionSource-Mitglied, da er weiß, dass es nur gesetzt wird, wenn eine Antwort abgerufen wurde. Genau an diesem Punkt wissen Sie, dass der Autorisierungsstatus ausgefüllt wird (wenn alles erfolgreich war), und so können Sie ihre Werte als Teil des AuthorizationResult zurückgeben.

Die zweite öffentliche Methode NotifyCallback ist, wie ich bereits erwähnt habe, das, was die Klasse MainActivity zurückrufen soll, sobald Ihre obige completedIntent ausgeführt wurde. In dieser Methode wollen Sie die Antwort überprüfen, den Status entsprechend aktualisieren und bei Erfolg eine Token-Austauschanforderung durchführen:

if (!intent.TryGetAuthStateFromExtra(out authorizationState)){ taskCompletionSource.SetResult(false); return;}AuthorizationResponse authorizationResponse = AuthorizationResponse.FromIntent(intent);AuthorizationException authorizationException = AuthorizationException.FromIntent(intent);authorizationState.Update(authorizationResponse, authorizationException);if (authorizationException != null){ taskCompletionSource.SetResult(false); return;}authorizationService.PerformTokenRequest(authorizationResponse.CreateTokenExchangeRequest(), ReceivedTokenResponse);

Hier können Sie sehen, dass ich in den fehlgeschlagenen Fällen das Ergebnis von taskCompletionSource auf false gesetzt habe, was die obige RequestAuthorization Methode entsperrt. Außerdem nimmt die Methode PerformTokenRequest einen Delegaten ReceivedTokenResponse auf, der ausgeführt wird, sobald sie abgeschlossen ist. Diese Methode sieht wie folgt aus:

private void ReceivedTokenResponse(TokenResponse tokenResponse, AuthorizationException authorizationException){ authorizationState.Update(tokenResponse, authorizationException); taskCompletionSource.SetResult(true);}

Zu diesem Zeitpunkt sollten Sie alle benötigten Berechtigungsdaten haben und können daher den Status entsprechend aktualisieren (wo Sie die Werte finden, die von der Anmeldemethode zurückgegeben werden sollen) und das Ergebnis so einstellen, dass die taskCompletionSource Aufgabe freigegeben wird.

Implementieren Sie die Authentifizierung in Ihre Xamarin-Benutzeroberfläche

Wenn Sie es wünschen, können Sie alle Verweise auf die „Floating Action Bar“ (auch als „FAB“ bezeichnet) in den Hauptaktivitätsklassen/Axml-Dateien entfernen, da sie in diesem Stadium unnötig aufgebläht sind.

Um dem Benutzer die Anmeldung zu ermöglichen, müssen Sie diese Funktionalität nun in die Benutzeroberfläche implementieren. In Anbetracht des Standarddesigns wäre der intuitivste Weg, dies zu tun, ein Element zum Überlaufmenü in der oberen rechten Ecke hinzuzufügen. Sie können dies tun, indem Sie die menu_main.xml-Datei im Ordner „Resources -> menu“ bearbeiten und dieses Element als untergeordnetes Element des menu-Tags hinzufügen:

<item android:id="@+id/action_signin" android:orderInCategory="100" android:title="@string/action_signin" app:showAsAction="never" />

Mit diesem Code haben Sie eine neue Option mit einem Titel erstellt, der in der String-Ressourcen-Datei definiert wird. Wie bereits erwähnt, ist es bei Android die beste Praxis, alle für den Benutzer sichtbaren Texte in der String-Ressourcen-Datei abzulegen. Um diese Daten zu deklarieren, bearbeiten Sie die Datei strings.xml im Ordner „Resources -> values“ und fügen Sie diese Zeilen hinzu:

<string name="action_signin">Sign In</string><string name="welcome_message_format">Hi, %1$s!</string>

Nicht nur, dass ich hier einen String für die Schaltfläche „Sign In“ deklariert habe, sondern ich habe auch oben einen String für eine Willkommensnachricht an den Benutzer hinzugefügt, sobald er sich angemeldet hat. Das entspricht dem C#-Code this string would be „Hallo, {0}!“`, wobei der Platzhalter vom Typ String ist.

Bei allen Aktualisierungen dieser Ressourcen-basierten Dateien wird die Klasse Resource.designer.cs automatisch mit neuen IDs für jedes von Ihnen erstellte Objekt neu generiert, auf die Sie in Ihrem Code verweisen können. Wenn dies bei einer bestimmten Datei nicht funktioniert, wählen Sie sie im Projektmappen-Explorer aus und sehen Sie sich das Fenster Eigenschaften an. Vergewissern Sie sich, dass die CustomTool-Eigenschaft auf den Wert MSBuild:UpdateGeneratedFiles gesetzt ist, da dieser wahrscheinlich fehlt und verhindert, dass die Designerdatei sie als Ressource erkennt.

Fügen Sie als Nächstes eine ProgressBar zur vorhandenen activity_main.axml-Layoutdatei hinzu:

<ProgressBar android:id="@+id/signin_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:paddingBottom="12dp" android:visibility="gone" />

Diese ProgressBar (oder Spinner, je nach Fall) hat eine ID, auf die Sie mit Code verweisen können, und ist so eingerichtet, dass sie in der Mitte des Bildschirms sitzt. Die Sichtbarkeit ist für den Moment auf „gone“ gesetzt, aber sobald dein Autorisierungscode läuft, kannst du ihn auf „visible“ setzen und den Benutzer darüber informieren, dass die App beschäftigt ist.

Jetzt hast du eine Schaltfläche, um die Authentifizierung zu öffnen, und eine Fortschrittsanzeige, um den Benutzer darüber zu informieren, dass die App beschäftigt ist, es ist Zeit, sie zu benutzen. Fügen Sie innerhalb Ihrer MainActivity-Klasse die folgende Eigenschaft zum Activity-Attribut (über dem Klassenkopf) hinzu:

LaunchMode = LaunchMode.SingleTask

Diese Eigenschaft stellt sicher, dass es nur eine Instanz der MainActivity-Klasse gibt und Sie nicht ständig neue öffnen müssen. Fügen Sie danach eine statische Membervariable für die oben geschriebene AuthorizationProvder hinzu und erstellen Sie eine Instanz davon innerhalb der bestehenden Überschreibung der OnCreate-Methode. Beachten Sie, dass dies nach dem bestehenden Code innerhalb der Methode erfolgen sollte:

private static AuthorizationProvider authorizationProvider;protected override void OnCreate(Bundle savedInstanceState){ ... authorizationProvider = new AuthorizationProvider(this);}

Nächste Überschreibung der Methode OnNewIntent. Wenn ein neuer Intent dieser Klasse erstellt wird (d.h. wenn das externe Anmeldefenster zurückkehrt), rufen Sie die Methode NotifyCallback aus AuthorizationProvider auf. Dazu gehört auch eine kurze Überprüfung, um sicherzustellen, dass es sich um den erwarteten Ablauf handelt:

protected override void OnNewIntent(Intent intent){ base.OnNewIntent(intent); if (intent != null && intent.Data.Path.Equals("/callback", StringComparison.OrdinalIgnoreCase)) { authorizationProvider.NotifyCallback(intent); }}

Nun fügen Sie den Code hinter dem hinzugefügten Menüpunkt hinzu. Fügen Sie in der bestehenden Überschreibung der Methode OnOptionsItemSelected eine Anweisung if mit einem Aufruf einer neuen Methode hinzu, die den Anmeldevorgang wie folgt abwickelt:

if (id == Resource.Id.action_signin){ OnSignInAttempted(); return true;}

Diese neue Methode beginnt damit, dass sie die ProgressBar sichtbar macht, die Sie soeben hinzugefügt haben; um eine beliebige Komponente aus der Layoutdatei abzurufen, verwenden Sie die generische Methode FindViewById und geben die ID der Komponente als Argument ein. Danach rufen Sie die Methode SignInAsync auf und warten das Ergebnis ab. Sobald der Aufruf zurückkehrt, wird das Ergebnis als autorisiert überprüft. Wenn die Autorisierung aus irgendeinem Grund fehlgeschlagen ist, erscheint ein Fehlerdialog, und der Fortschrittsanzeiger verschwindet wieder. Ich verzichte jetzt auf die Beschreibung des Erfolgsfalles, da Sie in diesem Fall noch eine Weiterleitung benötigen:

private async void OnSignInAttempted(){ ProgressBar signInProgress = FindViewById<ProgressBar>(Resource.Id.signin_progress); signInProgress.Visibility = ViewStates.Visible; AuthorizationResult authorizationResult = await authorizationProvider.SignInAsync(); if (!string.IsNullOrWhiteSpace(authorizationResult.AccessToken) && authorizationResult.IsAuthorized) { // Placeholder: Success case } else { // Display an error to the user AlertDialog authorizationErrorDialog = new AlertDialog.Builder(this) .SetTitle("Error!") .SetMessage("We were unable to authorize you.") .Create(); authorizationErrorDialog.Show(); signInProgress.Visibility = ViewStates.Gone; }}

Wenn der Benutzer authentifiziert ist, sollten Sie ihn auf die nächste Seite Ihrer Erfahrung weiterleiten. Wenn Sie sich daran erinnern, dass jede Seite durch eine Aktivität dargestellt wird, müssen Sie jetzt eine neue erstellen.

Zu Beginn müssen Sie im Ordner „Ressourcen -> Layout“ die neue Aktivitäts-Layoutdatei „activity_dashboard.axml“ erstellen. Am einfachsten geht das über die Option Neues Element… im Kontextmenü und die Auswahl der Vorlage „Android Layout“. Fügen Sie in Ihrer neuen Layout-Datei eine einfache TextView-Komponente hinzu, um einen Text wie diesen anzuzeigen:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:id="@+id/welcome_message" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /></LinearLayout>

In diesem Schnipsel haben Sie eine TextView-Komponente mit einer referenzierbaren ID, die in der Mitte der Seite zentriert ist und eine Willkommensnachricht anzeigt. Als Nächstes erstellen Sie eine entsprechende Aktivitätsklasse „DashboardActivity“, indem Sie die Option „New Item…“ im Kontextmenü des Projekts im Projektmappen-Explorer wählen und die Vorlage „Activity“ auswählen. Um diese Klasse mit ihrer Layout-Datei zu verknüpfen, müssen Sie die SetContentView-Funktion in der generierten OnCreate()-Methode (unter dem Aufruf der geerbten Basismethode) aufrufen:

SetContentView(Resource.Layout.activity_dashboard);

Um Ihre Willkommensnachricht zu personalisieren, möchten Sie den Namen des Benutzers an Ihre neue Aktivität übergeben. Wenn Sie sich daran erinnern, dass dies am besten mit Extras im Intent möglich ist, haben Sie eine Erweiterungsklasse erstellt, die diese Aufgabe übernimmt. Fügen Sie wie zuvor neue Methoden für ‚Put‘ und ‚Get‘ eines ‚Name‘-Extra in die IntentExtensions.cs-Datei ein, die Sie oben erstellt haben:

private const string NameKey = "Name";public static void PutNameExtra(this Intent intent, string name){ intent.PutExtra(NameKey, name);}public static string GetNameExtra(this Intent intent){ return intent.GetStringExtra(NameKey);}

Nutzen Sie nun diese erweiterte Funktionalität, um nach dem Aufruf von SetContentView, den Sie in der OnCreate()-Methode gemacht haben, den Namen des Benutzers abzurufen und den Text der TextView-Komponente entsprechend zu setzen:

string name = Intent.GetNameExtra();TextView welcomeMessage = FindViewById<TextView>(Resource.Id.welcome_message);welcomeMessage.Text = Resources.GetString(Resource.String.welcome_message_format, name);

In diesem Auszug wird beim Abrufen der TextView-Instanz deren Wert auf Ihre Willkommensnachricht gesetzt, die mit dem Android-Ressourcen-Äquivalent von string.Format() erstellt wird.

Damit ist Ihre Dashboard-Aktivität abgeschlossen, und Sie müssen sie nun aufrufen. Im Platzhalter für den Erfolgsfall, den ich in der OnSignInAttempted-Methode offen gelassen habe, können Sie dies durch Hinzufügen des folgenden Codes erreichen:

JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();JwtSecurityToken jsonToken = tokenHandler.ReadJwtToken(authorizationResult.IdentityToken);IEnumerable<Claim> claims = jsonToken?.Payload?.Claims;string name = claims?.FirstOrDefault(x => x.Type == "name")?.Value;Intent dashboardIntent = new Intent(this, typeof(DashboardActivity));dashboardIntent.PutNameExtra(name);StartActivity(dashboardIntent);Finish();

Der erste Block liest das Token und ruft den Namen des Benutzers ab (falls er existiert). Im zweiten Block wird ein neues Intent für die Dashboard-Aktivität erstellt, der Name des Benutzers wird in diesem Intent mit der oben definierten Erweiterungsmethode gespeichert, und dann wird die Aktivität gestartet (d. h. es wird zu ihr navigiert). Um zu verhindern, dass der Benutzer anschließend zu dieser Seite zurücknavigiert, endet der Code mit dem Aufruf der Finish()-Methode.

Starten Sie Ihre Android-App

Jetzt ist es an der Zeit, Ihre Anwendung mit dem von Ihnen gewählten Gerät zu starten!

Wenn Sie das Debugging mit dem Emulator durchführen, sollte dies so einfach sein wie das Drücken von F5, wodurch zuerst der Emulator geöffnet und geladen wird, und dann der Code in ihm bereitgestellt wird. Nebenbei bemerkt müssen Sie den Emulator zwischen den Start-/Debug-Versuchen nicht schließen, da er die Anwendung nur neu bereitstellen muss.

Wenn Sie mit einem Gerät debuggen, das noch nicht für diesen Zweck verwendet wurde, müssen Sie das Gerät zuerst einrichten. Aktivieren Sie dazu die Entwickleroptionen und schalten Sie im neuen Menü die Option „Android-Debugging“ unter der Überschrift „Debugging“ ein. Schließen Sie dann das Gerät an, akzeptieren Sie den Dialog auf Ihrem Gerät, der bestätigt, dass es sich um eine sichere Debugging-Verbindung handelt, und Sie sollten in der Lage sein, Ihre App mit F5 bereitzustellen und auszuführen. Beachten Sie, dass physische Geräte Vorrang vor dem Emulator haben und nach dem Einstecken als Standard-Debugging-Option verwendet werden.

Nach dem Bereitstellen und Laden Ihrer App werden Sie von der Standardvorlage für eine Seite begrüßt. Öffnen Sie das Menü in der oberen rechten Ecke, um sich anzumelden, und sobald Sie Ihre Daten eingegeben haben, sollten Sie zu dieser Seite mit dem sich drehenden Fortschrittsbalken zurückkehren, bevor Sie automatisch zur Dashboard-Seite mit Ihrer Willkommensnachricht weitergeleitet werden:

Erfahren Sie mehr über Xamarin, OpenID Connect und sichere Authentifizierung

Wenn Sie all diese Schritte befolgt haben, haben Sie nun eine grundlegende Android-App, die mit Xamarin.Android erstellt wurde, mit einer voll funktionsfähigen Benutzerauthentifizierung auf Basis von OpenID und dem Okta-Dienst. Von hier aus können Sie die Dashboard-Aktivität leicht erweitern, um Ihre Funktionalität zu implementieren.

Um den Code aus diesem Beitrag in vollem Umfang zu sehen, besuchen Sie unsere GitHub-Seite.

Wenn dieses Tutorial Ihren Appetit auf die Xamarin-Entwicklung geweckt hat und Sie mehr erfahren möchten, empfehle ich Ihnen, einen Blick auf diese anderen großartigen Artikel zu werfen:

  • Hinzufügen von Identitätsmanagement zu Ihrer Android-App
  • Zufügen von Authentifizierung zu Ihrer Xamarin.Forms-App mit OpenID Connect
  • Erstellen einer App für iOS und Android mit Xamarin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.