Ben jij een .NET ontwikkelaar die altijd al eens een mobiele applicatie heeft willen maken? Of misschien heb je geprobeerd native mobiele apps te bouwen met Android of iOS, maar vond je de talen maar niks? Nou, dan heb je geluk! De .NET wereld is gezegend met Xamarin; een set van tools waarmee je mobiele apps kunt bouwen voor Android, iOS en Windows binnen Visual Studio.
Xamarin heeft twee hoofdsmaken: Xamarin platform (Xamarin.iOS en Xamarin.Android) en Xamarin.Forms. Met Xamarin.Forms kan een overgroot deel van je business logica en gebruikersinterface worden geschreven binnen één gedeeld project dat volledig functionerende apps produceert op alle 3 de iOS, Android en de Windows (UWP) besturingssystemen. Xamarin platform, aan de andere kant, is zeer platform-specifiek werk en is meer verwant aan het schrijven van native apps, maar met C#.
In deze tutorial, zal ik dieper ingaan op het Xamarin platform en de Android besturingssysteem toolset bekend als Xamarin.Android. Het algemene doel is om u in staat te stellen een eenvoudige native Android app te maken met basis gebruikersauthenticatie inbegrepen.
- Stel Visual Studio en uw omgeving in
- Verifieer uw Android omgeving in Visual Studio
- Maak een Xamarin App
- Verwen jezelf met het Xamarin Project
- Voeg gebruikersauthenticatie toe aan Xamarin met OpenID Connect
- Opzetten van uw Okta Applicatie
- Maak de Authenticatieprovider
- Implementeer Authenticatie in je Xamarin Interface
- Run Your Android App
- Lees meer over Xamarin, OpenID Connect en veilige authenticatie
Stel Visual Studio en uw omgeving in
Om mee te volgen heeft u een kopie van Visual Studio nodig, plus de ‘Mobiele ontwikkeling met .NET’ werkbelasting. U kunt deze functie inschakelen bij de eerste installatie van Visual Studio of openen via het menu Extra -> Tools en functies ophalen…:
Bij het testen en uitvoeren van uw app hebt u de keuze om dit te doen met een Android-emulator die op uw ontwikkelmachine draait, of door rechtstreeks verbinding te maken met een bestaand Android-toestel. Er is geen juiste optie hier en verschillende ontwikkelaars geven de voorkeur aan verschillende vormfactoren. Als u kiest voor de eerste optie, moet u ervoor zorgen dat zodra u de werkbelasting hebt geselecteerd dat op het rechterdeelvenster (‘Installatiedetails’) de selectievakjes voor Intel Hardware Accelerated Execution Manager en Google Android Emulator zijn geselecteerd (zoals hierboven te zien).
Verifieer uw Android omgeving in Visual Studio
Om te controleren of alles correct is geïnstalleerd en geconfigureerd, gaat u naar ‘Extra -> Opties -> Xamarin -> Android Instellingen’ en controleert u of uw Java Development Kit Location en Android SDK Location paden geldig zijn (d.w.z. een groen vinkje hebben).d.w.z. een groen vinkje hebben):
Als een van beide ontbreekt moet u handmatig de Java Development Kit respectievelijk de Android SDK installeren.
Maak een Xamarin App
Start met het maken van een nieuw project en selecteer de ‘Android App (Xamarin)’ master template (te vinden onder het Android menu). Op de volgende pagina zult u de ‘Single View App’ optie willen kiezen omdat het een geweldige starter template is om mee te werken.
Met betrekking tot de Minimale Android Versie, dit is iets van uw persoonlijke keuze als de ontwikkelaar. Er is een trade-off hier tussen de mogelijkheid om de nieuwste en grootste API-functies in nieuwere versies te openen en het ondersteunen van uw klanten die oudere versies hebben. Om u te helpen deze beslissing te nemen, publiceert Google platform versie distributie gegevens die ze verzamelen als onderdeel van hun Distribution dashboard op een vrij regelmatige cadans. Mijn persoonlijke voorkeur gaat uit naar 5.0 of 6.0, afhankelijk van of dit een app is voor publiek gebruik of een in opdracht gemaakte app voor alleen bedrijfstelefoons (d.w.z. dat zij waarschijnlijk de laatste updates zullen hebben); in dit voorbeeld heb ik gekozen voor het laatste. Merk op dat deze versie verschilt van de Doel Android Versie en dat moet altijd worden ingesteld op de nieuwste vrijgegeven SDK versie, als alles minder zal niet worden aanvaard in de Google Play store.
Als je eenmaal hebt dit gemaakt alles wat overblijft is om de vereiste NuGet pakketten te importeren. Voor deze tutorial heb je nodig:
- Xamarin.OpenId.AppAuth.Android – Om de gebruiker te authenticeren zul je OpenID Connect standaard gebruiken (een verbetering van OAuth 2.0). De eenvoudigste manier om client code te implementeren die zich aan deze specificatie houdt is door gebruik te maken van de AppAuth client SDK voor Android, en behulpzaam heeft Xamarin een pakket van deze functionaliteit voor u beschikbaar gesteld om te gebruiken.
- System.IdentityModel.Tokens.Jwt – De methode van authenticatie hier maakt gebruik van JSON Web Tokens. Om de benodigde gegevens uit deze tokens te halen heeft u dit pakket nodig.
Bijkomend heeft u ook deze twee pakketten nodig omdat AppAuth daarop vertrouwt:
- Xamarin.Android.Support.v4
- Xamarin.Android.Support.CustomTabs
Verwen jezelf met het Xamarin Project
Als je nog nooit met Android hebt gewerkt, is een van de belangrijkste principes om je hoofd rond te krijgen het concept van een Activiteit. Activiteiten zijn componenten die worden gebruikt om je gebruikersinterface weer te geven; in hun meest basale vorm, kun je denken aan Activiteiten als gelijk aan pagina’s in een app waartussen de gebruiker kan navigeren. Een Activiteit wordt in code voorgesteld door een klasse, maar net zoals een pagina in ASP.NET kan (en zal) je er een XML-gebaseerd lay-out bestand (een .axml bestand) aan koppelen voor een toonbaar ontwerp. Alle nieuwe projecten maken een ‘MainActivity.cs’ en ‘activity_main.axml’ bestand aan om mee te beginnen als de eerste Activiteit (d.w.z. pagina) die wordt uitgevoerd bij het openen van de app. Dit kan worden gewijzigd in elke andere activiteit door gebruik te maken van de eigenschap MainLauncher = true
in het Activity
-attribuut van de klasse.
Resources zijn ontworpen om te worden behandeld binnen hun eigen directory en volgen een enigszins strikte naamgevingsconventie. Ik zou sterk aanraden om zoveel mogelijk van je resources in deze directory op te slaan als haalbaar is, omdat dit het hergebruik van deze variabelen vereenvoudigt voor je verdere ontwikkeling. In de ‘values’ directory van de Resources directory vind je bestanden met specifieke doelen:
- Strings.xml – Bevat alle gebruikersgerichte strings. Deze is vooral belangrijk om te gebruiken omdat het u in staat stelt om uw strings te lokaliseren voor een wereldwijd publiek.
- Styles.xml – Waar u de attributen vindt voor de styling van uw ontwerpobjecten; zie het als een CSS-bestand.
- Colors.xml – Een plaats om verwijzingen op te slaan naar de kleuren die u het vaakst gebruikt als onderdeel van uw styling.
- Dimens.xml – Zoals de naam al doet vermoeden, definieert u hier de vaste afmetingen voor de lay-out van uw app.
De andere opvallende mappen volgen geen naamconventie voor hun bestanden, maar ze moeten bepaalde bestandstypen bevatten:
- Layout – Locatie voor het opslaan van uw .axml-bestanden. Deze bestanden definiëren volledige Activiteit lay-outs, lay-out componenten die u programmatisch genereert en vult, lay-outs van dialoog (waarschuwing) boxen, enz.
- Menu – Waar vindt u definities van menu’s en hun items. Dit zijn .xml-bestanden met
menu
als root-element enitem
child-elementen, die kunnen worden gegroepeerd metgroup
-elementen. De meest voorkomende menu’s die je tegenkomt zijn het overloop menu (van de drie verticale puntjes knop) of het navigatie menu (van de home ‘hamburger’ knop). - Mipmap – Waar je afbeeldingen wilt definiëren die geschaald moeten worden afhankelijk van de scherm dichtheid, dat wil zeggen die waarnaar verwezen wordt door andere apps, en niet intern gebruikt worden. Het icoon van de app is de meest voorkomende inhoud die je in de mipmap directories zou zetten.
- Drawable – Zit niet standaard in een standaard project template, maar kan zelf gemaakt worden. Dit is waar u uw afbeeldingen/tekenbare inhoud opslaat om te gebruiken binnen de app, b.v. splash screen, aangepaste voortgangsbalk ontwerpen, iconen gebruikt binnen de app, etc.
Ten slotte, als u ruwe inhoud bestanden heeft die u wilt gebruiken als onderdeel van uw applicatie (b.v. een tekst of lettertype bestand), dan is de Assets map waar u ze plaatst. Als Assets zullen deze bestanden worden ingezet met uw app voor u om toegang te krijgen met de Asset Manager.
Om meer te leren over Assets en Resources en hoe ze te gebruiken, zijn er handige ‘Over’ tekst bestanden in elke directory van een nieuw gemaakt project.
Voeg gebruikersauthenticatie toe aan Xamarin met OpenID Connect
De meeste apps vereisen tegenwoordig een vorm van gebruikersidentificatie om de ontwikkelaar in staat te stellen op maat gemaakte ervaringen aan te bieden en op zijn beurt de gebruiker in staat te stellen hun gegevens te bewaren op verschillende apparaten/installaties. Daarnaast is er de kwestie van toegangscontrole die nuttig kan zijn om een subset van gebruikers te autoriseren voor extra functionaliteit. Natuurlijk kan dit nogal een omslachtige taak zijn, vooral als je in de business zit van het creëren van apps en je hebt een nieuw systeem nodig voor elke app. Gelukkig kun je met Okta in een paar minuten een applicatie opzetten en dan is al het harde werk voor je gedaan! De Okta service is OAuth 2.0 compliant en een gecertificeerde OpenID Connect Provider, en werkt dus perfect samen met de AppAuth client SDK voor al uw authenticatie en autorisatie behoeften.
Opzetten van uw Okta Applicatie
Ten eerste moet u een nieuwe applicatie opzetten in uw Okta account voor dit project. Als u er nog geen hebt, kunt u heel eenvoudig een nieuwe gratis ontwikkelaarsaccount aanmaken.
Als dat is gebeurd en u bent ingelogd op het ontwikkelaarsdashboard, noteert u de Org URL, want die hebben we later nodig:
Vanuit het dashboard gaat u naar het tabblad ‘Applicaties’ en van daaruit naar ‘Applicatie toevoegen’. U maakt een native Android-toepassing, dus het is het beste om het platformsjabloon ‘Native’ te kiezen.
Vanaf deze pagina voegt u een naam toe voor uw toepassing en laat u de rest als standaard staan. Eenmaal opgeslagen, noteer uw Login redirect URIs en Client ID omdat u deze hierna nodig heeft.
Om deze drie waarden van Okta in de toekomst gemakkelijk te kunnen gebruiken, zou ik aanraden om ze in hun eigen statische klasse te plaatsen in een nieuwe map voor de authenticatielogica:
public static class Configuration{ public const string ClientId = "{yourClientId}"; public const string LoginRedirectUri = "{yourRedirectUri}"; public const string OrgUrl = "https://{yourOktaDomain}";}
Maak de Authenticatieprovider
Laten we de boilerplate-code eens uit de weg ruimen. U moet de app configureren om hem op de hoogte te stellen van uw omleidings URI-schema. Het schema is uw login redirect URI (in standaard omgekeerde domeinnaam vorm) zonder het pad. Bijvoorbeeld, uit de bovenstaande screenshot mijn schema zou zijn ‘com.oktapreview.dev-123456’.
De eenvoudigste manier om dit te doen is om de onderstaande intent filter snippet invoegen in uw AndroidManifest.xml
bestand in de Properties
map van uw oplossing. Voeg de volgende XML toe in de Application
-tag en verander de schemawaarde in je eigen waarde:
<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>
Je moet ook het model voor het resultaat van je autorisatie definiëren met een eenvoudige klasse. Hoewel ik niet alle waarden die ik hieronder heb geschreven in de code zal gebruiken, zal ik laten zien hoe je ze kunt invullen zodat je ze daarna kunt gebruiken. Aangezien dit deel uitmaakt van het model van uw applicatie, maakt u een map genaamd Models
en voegt u daar een AuthorizationResult.cs
klasse aan toe. Voeg vervolgens de volgende code toe:
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 heeft geen globale toestand voor u om mee te werken, en dus als u eenvoudige waarden tussen activiteiten wilt doorgeven, is de beste manier om dit te doen met de Extras
functionaliteit op het Intent
object. Een Intent
is een voorgedefinieerde klasse in Android en een ander kern concept om te begrijpen. Het is de abstractie van een uit te voeren operatie (d.w.z. uw ‘bedoelingen’), en om naar een andere activiteit te navigeren moet u een instantie van deze klasse maken met welke activiteit u ‘van plan bent’ om naar toe te gaan. De Extras-eigenschappen op het Intent
-object zijn in feite niet meer dan een woordenboek van sleutels tot objectwaarden en worden benaderd door Put
en getypte Get
-methoden.
Hoewel deze methoden het gebruik relatief duidelijk en gemakkelijk houden, vind ik het persoonlijk prettig om alle toegang tot ze binnen hun eigen klasse te houden (om precies te zijn, een extensions-klasse), om een betere scheiding van belangen te handhaven. Dit is uiterst nuttig omdat je geen toegang tot de sleutels in verschillende klassen nodig hebt en omdat je type-veiligheid kunt garanderen bij het invoeren en ophalen van deze waarden. In je authorization provider wil je: de AuthState
opslaan, kunnen controleren of hij er is, en hem teruggeven als dat het geval is. Maak een nieuwe map genaamd Extensions
in de root van de oplossing. Voeg dan een nieuwe klasse toe genaamd IntentExtensions.cs
. Maak de klasse public
en static
, en voeg dan de volgende code toe in de klasse:
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;}
Nu is het tijd om de autorisatie provider te definiëren, AuthorizationProvider.cs
in de Authentication
map die je eerder hebt gemaakt voor de Configuration.cs
klasse. Verwijder eerst alle using
statements in de nieuw aangemaakte class, declareer dan de configuratie als static readonly
variabelen:
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" };
Het configuratie eindpunt is een standaard in OpenID als het discovery eindpunt om alles te vinden wat ondersteund wordt. Merk op dat ik hier heb geschreven met de ‘standaard’ provider naam. Als je een andere provider hebt, zul je dit hier moeten veranderen. Merk ook op dat dit de Android.Net
smaak van de Uri klasse gebruikt, en niet de System
versie – je zult de eerste moeten toevoegen aan je gebruik of het type volledig moeten kwalificeren om dit te laten werken. De Scopes-variabele definieert, net als elk ander OpenID-systeem, waartoe we toegang hebben.
Volgende moet u uw lidvariabelen declareren:
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>();
Een korte uitleg over elk:
- Het autorisatieverzoek en de voltooide intentie zijn parameters die zijn aangemaakt voor gebruik bij het maken van de autorisatieoproep. Ik heb ze hier als globale variabelen geschreven om de hoeveelheid parameters die in verschillende methoden moeten worden doorgegeven tot een minimum te beperken.
- De variabele authorizationState, zoals deze wordt genoemd, definieert de huidige gegeven autorisatiestatus.
- De variabele authorizationService bevat een instantie van de autorisatieservice.
- De contextvariabele hier is van de aanroepende activiteit, zodat u ernaar kunt verwijzen wanneer dat nodig is.
- Tot slot stelt de taskCompletionSource u in staat al deze aanroepen asynchroon te doen en na afloop terug te keren.
Nu moet u de waarden van deze readonly variabelen in uw constructor definiëren, en de publieke methoden declareren die uw code zal aanroepen:
public AuthorizationProvider(Context context){ authorizationService = new AuthorizationService(context); this.context = context;}public async Task<AuthorizationResult> SignInAsync(){ ...}public void NotifyCallback(Intent intent){ ...}
De SignInAsync-methode is zoals u misschien al geraden hebt een asynchrone methode om een gebruiker aan te melden. Deze methode retourneert de AuthorizationResult
-klasse die u eerder hebt geschreven. NotifyCallback
aan de andere kant is voor de aanroepende activiteit, zodra het is teruggekeerd van de externe aanmeldingspagina, om terug te bellen naar de autorisatie provider en het te laten weten dat het klaar is. De aanmeldingsmethode heb ik uitgesplitst in meerdere subroutines en ziet er als volgt uit:
AuthorizationServiceConfiguration serviceConfiguration = await AuthorizationServiceConfiguration.FetchFromUrlAsync(ConfigurationEndpoint);BuildAuthorizationRequest(serviceConfiguration);BuildCompletedIntent(serviceConfiguration);return await RequestAuthorization();
Hierin heb je de serviceconfiguratie gedefinieerd, het autorisatieverzoek gebouwd en de intentie om te bellen zodra de autorisatie is voltooid, en dan wacht je op het autorisatieverzoek. Het bouwen van het autorisatieverzoek gaat als volgt:
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();}
De taak van deze methode is om het AuthorizationRequest.Builder
-werk weg te abstraheren en het verzoek te maken. Vervolgens moet je de Intent
bouwen voor als de operatie is voltooid:
private void BuildCompletedIntent(AuthorizationServiceConfiguration serviceConfiguration){ Intent intent = new Intent(context, typeof(MainActivity)); intent.PutAuthStateExtra(new AuthState()); completedIntent = PendingIntent.GetActivity(context, authorizationRequest.GetHashCode(), intent, 0);}
De ‘bedoeling’ die je hier wilt uitvoeren is om terug te keren naar je MainActivity
met een nieuwe AuthState eraan gekoppeld. Als laatste in deze flow moet het verzoek worden uitgevoerd:
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 };}
Omdat PerformAuthorizationRequest
synchroon is en leeg teruggeeft, wacht de code op het taskCompletionSource
lid, wetende dat het pas zal worden ingesteld als een antwoord is opgehaald. Op dit zelfde punt weet je dat de authorization state zal worden ingevuld (als alles is gelukt), en dus kun je hun waarden retourneren als onderdeel van het AuthorizationResult.
De tweede publieke methode NotifyCallback
, zoals ik al eerder zei, is wat je wilt dat de MainActivity
klasse terugroept, zodra je bovenstaande completedIntent
hebt uitgevoerd. In deze methode wil je het antwoord verifiëren, de status op de juiste manier bijwerken, en indien succesvol, een token uitwisselingsverzoek uitvoeren:
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 kun je zien dat ik in de mislukte gevallen het resultaat van taskCompletionSource op false heb gezet, en dat zal de blokkade van de RequestAuthorization
methode hierboven opheffen. Ook neemt de methode PerformTokenRequest
een delegate op, ReceivedTokenResponse
, die wordt uitgevoerd zodra de methode is voltooid. Deze methode ziet er als volgt uit:
private void ReceivedTokenResponse(TokenResponse tokenResponse, AuthorizationException authorizationException){ authorizationState.Update(tokenResponse, authorizationException); taskCompletionSource.SetResult(true);}
Op dit punt zou u alle autorisatiegegevens moeten hebben die u nodig hebt, en dus kunt u de status op de juiste manier bijwerken (waar u de waarden vindt om terug te geven van de aanmeldingsmethode) en het resultaat instellen om de taskCompletionSource
taak te deblokkeren.
Implementeer Authenticatie in je Xamarin Interface
Voel je vrij om alle verwijzingen naar de ‘Floating Action Bar’ (ook wel geschreven als ‘FAB’) binnen de hoofdactiviteit klasse/axml bestanden te verwijderen, omdat ze in dit stadium onnodige bloat zijn.
Om de gebruiker in staat te stellen zich aan te melden, moet je nu deze functionaliteit in de UI implementeren. Gezien het standaard ontwerp, de meest intuïtieve manier om dit te doen zou zijn om een item toe te voegen aan de overloop menu in de rechterbovenhoek. U kunt dit doen door het menu_main.xml
bestand in de ‘Resources -> menu’ map te bewerken, en dit item toe te voegen als een kind van de menu
tag:
<item android:id="@+id/action_signin" android:orderInCategory="100" android:title="@string/action_signin" app:showAsAction="never" />
Met deze code heeft u een nieuwe optie gemaakt met een titel die moet worden gedefinieerd in het string resources bestand. Zoals eerder vermeld is het bij Android het beste om alle tekst die op de gebruiker gericht is in het string resources bestand te zetten. Om deze gegevens te declareren, bewerkt u het strings.xml
bestand in de ‘Resources -> waarden’ map en voegt u de volgende regels toe:
<string name="action_signin">Sign In</string><string name="welcome_message_format">Hi, %1$s!</string>
Niet alleen heb ik hier een string voor de ‘Aanmelden’ knop gedeclareerd, maar ik heb hierboven ook een string toegevoegd voor een welkomstbericht aan de gebruiker zodra deze zich heeft aangemeld. Het equivalent van de C# code van this string would be
“Hi, {0}!”`, waarbij de plaatshouder van het type string is.
Noteer op dat bij alle updates van deze op Resources gebaseerde bestanden, de Resource.designer.cs klasse automatisch wordt geregenereerd met nieuwe ID’s voor elk object dat je hebt gemaakt, waarnaar verwezen kan worden binnen je code. Als dit niet werkt voor een bepaald bestand, selecteer het dan in Solution Explorer en kijk naar het Properties venster. Zorg ervoor dat de CustomTool
eigenschap is ingesteld op de waarde MSBuild:UpdateGeneratedFiles
, omdat deze waarschijnlijk ontbreekt en voorkomt dat het designer-bestand het herkent als een resource.
Volgende voeg een ProgressBar
toe aan het bestaande activity_main.axml
layout-bestand:
<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" />
Deze ProgressBar
(of spinner, zoals het geval is), heeft een ID waarnaar je kunt verwijzen met code, en is ingesteld om in het midden van het scherm te staan. De zichtbaarheid is ingesteld op weg voor nu, maar zodra uw autorisatie code draait kun je het op zichtbaar zetten en de gebruiker informeren dat de app bezig is.
Nu heb je een knop om authenticatie te openen en een voortgang spinner om de gebruiker te informeren dat de app bezig is, is het tijd om ze te gebruiken. Voeg binnen uw MainActivity
klasse de volgende eigenschap toe aan het Activity
attribuut (boven de koptekst van de klasse):
LaunchMode = LaunchMode.SingleTask
Deze eigenschap zorgt ervoor dat er slechts één instantie van de MainActivity
klasse is, en dat u niet steeds nieuwe instanties hoeft te openen. Zodra u dit hebt gedaan, voegt u een statische lidvariabele toe voor de AuthorizationProvder
die u hierboven hebt geschreven en maakt u er een instantie van binnen de bestaande override van de OnCreate
-methode. Merk op dat dit moet gebeuren na de bestaande code binnen de methode:
private static AuthorizationProvider authorizationProvider;protected override void OnCreate(Bundle savedInstanceState){ ... authorizationProvider = new AuthorizationProvider(this);}
Volgende, override de OnNewIntent
methode. Het doel hiervan is dat wanneer een nieuwe intent van deze klasse wordt gemaakt (d.w.z. wanneer het externe aanmeldingsvenster terugkeert), u de methode NotifyCallback
aanroept vanuit de AuthorizationProvider
. Hierin is ook een snelle controle opgenomen om er zeker van te zijn dat dit de verwachte flow is:
protected override void OnNewIntent(Intent intent){ base.OnNewIntent(intent); if (intent != null && intent.Data.Path.Equals("/callback", StringComparison.OrdinalIgnoreCase)) { authorizationProvider.NotifyCallback(intent); }}
Nu voeg je de code toe achter het menu-item dat je hebt toegevoegd. Voeg in de bestaande override van de methode OnOptionsItemSelected
een if
-instructie toe met een oproep naar een nieuwe methode die het aanmeldingsproces als volgt zal afhandelen:
if (id == Resource.Id.action_signin){ OnSignInAttempted(); return true;}
Deze nieuwe methode zal beginnen met het zichtbaar maken van de ProgressBar
die u enkele ogenblikken geleden hebt toegevoegd; om een willekeurige component uit het lay-outbestand op te halen, gebruikt u de generieke methode FindViewById
en voert u als argument de ID van de component in. Hierna roept u de methode SignInAsync
aan en wacht op het resultaat. Zodra de aanroep is teruggekeerd, wordt het resultaat geverifieerd als geautoriseerd. Als deze autorisatie om wat voor reden dan ook mislukt, verschijnt er een foutmelding en verdwijnt de progress spinner weer. Ik laat de details van het succesgeval nu even voor wat ze zijn, omdat je in dat geval nog ergens naar toe moet:
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; }}
Wanneer de gebruiker is geauthenticeerd, moet je hem doorverwijzen naar de volgende pagina van je ervaring. Als u zich herinnert dat elke pagina wordt weergegeven door een activiteit, moet u nu een nieuwe maken.
Om te beginnen moet u in de map ‘Hulpmiddelen ->lay-out’ het nieuwe activiteitenlay-outbestand ‘activity_dashboard.axml’ maken. De makkelijkste manier om dit te doen is door naar de New Item… optie te gaan in het context menu en het ‘Android Layout’ sjabloon te selecteren. Voeg in uw nieuwe lay-outbestand een eenvoudige TextView-component toe om tekst weer te geven zoals deze:
<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 deze snippet hebt u een TextView
-component met een referentie-ID die in het midden van de pagina is gecentreerd en een welkomstbericht weergeeft. Maak vervolgens een bijbehorende activity class ‘DashboardActivity’ aan door in de solution explorer in het contextmenu van het project de optie ‘New Item…’ te kiezen en het template ‘Activity’ te kiezen. Om deze klasse aan het opmaakbestand te koppelen, moet u de functie SetContentView aanroepen in de gegenereerde OnCreate()
-methode (onder de geërfde aanroep van de basismethode):
SetContentView(Resource.Layout.activity_dashboard);
Om uw welkomstboodschap te personaliseren, wilt u de naam van de gebruiker doorgeven aan uw nieuwe activiteit. Als u zich herinnert dat u dit het beste kunt doen met Extras op de bedoeling, en dat u een extensionsklasse hebt gemaakt om dit af te handelen. Voeg, net als eerder, nieuwe methoden toe voor ‘Put’ en ‘Get’ van een ‘name’ extra in het IntentExtensions.cs
-bestand dat u hierboven hebt gemaakt:
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);}
Nu gebruikt u deze uitgebreide functionaliteit, na de oproep aan SetContentView
die u hebt gedaan in de OnCreate()
-methode, om de naam van de gebruiker op te halen en de tekst van het TextView
-component op de juiste manier in te stellen:
string name = Intent.GetNameExtra();TextView welcomeMessage = FindViewById<TextView>(Resource.Id.welcome_message);welcomeMessage.Text = Resources.GetString(Resource.String.welcome_message_format, name);
In dit uittreksel wordt bij het ophalen van de TextView
-instantie de waarde ervan ingesteld op uw welkomstbericht, dat is gemaakt met behulp van het Android Resources-equivalent van string.Format()
.
Hiermee is uw dashboard activiteit voltooid, en u moet er nu een beroep op doen. In de plaatshouder voor het succesgeval dat ik open heb gelaten uit de OnSignInAttempted
methode, kunt u dit bereiken door de volgende code toe te voegen:
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();
Het eerste blok leest het token en haalt de naam van de gebruiker op (als deze bestaat). In het tweede blok wordt een nieuwe Intent
gemaakt voor de dashboardactiviteit, de naam van de gebruiker wordt opgeslagen in deze Intent
met behulp van uw hierboven gedefinieerde extensiemethode, en vervolgens wordt de activiteit gestart (d.w.z. er wordt naar genavigeerd). Om te voorkomen dat de gebruiker daarna terug navigeert naar deze pagina, eindigt de code met het aanroepen van de Finish()
methode.
Run Your Android App
Nu is het tijd om uw applicatie te lanceren met uw gekozen apparaat!
Als u debugt met behulp van de emulator, zou dit zo simpel moeten zijn als het indrukken van F5, die eerst de emulator zal openen en laden, en dan de code zal deployen naar de emulator. Je hoeft de emulator niet te sluiten tussen de run/debug pogingen, omdat het alleen nodig is om de app opnieuw te deployen.
Als je debugt met een apparaat dat nog niet eerder voor dit doel is gebruikt, moet je het apparaat eerst instellen. Doe dit door ontwikkelaarsopties in te schakelen en in het nieuwe menu ‘Android debugging’ in te schakelen onder het kopje ‘Debugging’. Hierna sluit u het apparaat aan, accepteert u het dialoogvenster op uw apparaat waarin wordt bevestigd dat dit een veilige debug-verbinding is, en kunt u uw app implementeren en uitvoeren met F5. Fysieke apparaten hebben een hogere prioriteit dan de emulator en zullen overschakelen naar de standaard debug-optie wanneer ze zijn aangesloten.
Zodra uw app is uitgerold en geladen, wordt u begroet door de standaard enkele pagina sjabloon. Open het menu in de rechterbovenhoek om u aan te melden, en zodra u uw gegevens hebt ingevoerd, zou u moeten terugkeren naar deze pagina met de draaiende voortgangsbalk voordat u automatisch naar de dashboardpagina wordt gestuurd met uw welkomstbericht:
Lees meer over Xamarin, OpenID Connect en veilige authenticatie
Als u al deze stappen hebt gevolgd, hebt u nu een basis Android-app die is gebouwd met Xamarin.Android, met volledig functionerende gebruikersauthenticatie gebaseerd op OpenID en de Okta service. Vanaf hier kunt u eenvoudig uitbreiden op het dashboard activiteit om uw functionaliteit te implementeren.
Om de code te zien van deze post in zijn geheel ga dan naar onze GitHub pagina.
Als deze tutorial je zin in Xamarin heeft geprikkeld en je wilt meer leren, dan raad ik je aan om ook eens naar deze andere artikelen te kijken:
- Voeg Identity Management toe aan je Android App
- Voeg Authenticatie toe aan je Xamarin.Forms App met OpenID Connect
- Bouw een App voor iOS en Android met Xamarin