Är du en .NET-utvecklare som alltid har velat göra en mobilapplikation? Eller kanske har du provat att bygga inbyggda mobilappar med Android eller iOS men inte gillat språken? Då har du tur! .NET-världen har blivit välsignad med Xamarin; en uppsättning verktyg som låter dig bygga mobilappar för Android, iOS och Windows inom Visual Studio.
Xamarin har två huvudvarianter: Xamarin Platform (Xamarin.iOS och Xamarin.Android) och Xamarin.Forms. Med Xamarin.Forms kan en stor del av affärslogiken och användargränssnittet skrivas i ett delat projekt som producerar fullt fungerande appar på alla tre operativsystemen iOS, Android och Windows (UWP). Xamarin-plattformen, å andra sidan, är mycket plattformsspecifikt arbete och är mer likt att skriva inhemska appar men med C#.
I den här handledningen kommer jag att titta närmare på Xamarin-plattformen och verktygslådan för operativsystemet Android som kallas Xamarin.Android. Det övergripande målet är att du ska kunna skapa en enkel inhemsk Android-app med grundläggande användarautentisering inkluderad.
- Sätt upp Visual Studio och din miljö
- Verifiera din Android-miljö i Visual Studio
- Skapa en Xamarin-app
- Förbered dig på Xamarin-projektet
- Add User Authentication to Xamarin with OpenID Connect
- Sätt upp din Okta-applikation
- Skapa autentiseringsleverantören
- Implementera autentisering i ditt Xamarin-gränssnitt
- Kör din Android-applikation
- Lär dig mer om Xamarin, OpenID Connect och säker autentisering
Sätt upp Visual Studio och din miljö
För att följa med behöver du ett exemplar av Visual Studio, plus arbetsuppgiften ”Mobile development with .NET”. Du kan antingen aktivera den här funktionen från den första installationen av Visual Studio eller komma åt den från menyalternativet ”Tools -> Get Tools and Features…”:
När du testar och kör din app kan du välja att göra det antingen med en Android-emulator som körs på utvecklingsmaskinen eller genom att direkt ansluta till en befintlig Android-enhet. Det finns inget rätt alternativ här och olika utvecklare föredrar olika formfaktorer. Om du väljer det förstnämnda alternativet måste du se till att när du har valt arbetsbelastningen att kryssrutorna för Intel Hardware Accelerated Execution Manager och Google Android Emulator är markerade i den högra rutan (”Installationsdetaljer”) (se ovan).
Verifiera din Android-miljö i Visual Studio
För att verifiera att allt har installerats korrekt och konfigurerats på rätt sätt går du till ”Verktyg -> Alternativ -> Xamarin -> Android-inställningar” och kontrollerar att sökvägarna för Java Development Kit Location och Android SDK Location är giltiga (dvs.dvs. har ett grönt kryss):
Om någon av dem saknas måste du manuellt installera Java Development Kit respektive Android SDK.
Skapa en Xamarin-app
Starta med att skapa ett nytt projekt och välj huvudmallen ”Android App (Xamarin)” (som du hittar under Android-menyn). På nästa sida vill du välja alternativet ”Single View App” eftersom det är en bra startmall att arbeta utifrån.
Med avseende på minsta Android-version är detta något som beror på ditt personliga val som utvecklare. Det finns en kompromiss här mellan att kunna få tillgång till de senaste och bästa API-funktionerna i nyare versioner och att stödja dina kunder som har äldre versioner. För att hjälpa dig att fatta detta beslut publicerar Google ganska regelbundet data om distribution av plattformsversioner som de samlar in som en del av sin Distribution dashboard. Min personliga preferens är mellan 5.0 eller 6.0 beroende på om detta är en app för allmänheten eller en beställd app endast för företagstelefoner (dvs. de kommer sannolikt att ha de senaste uppdateringarna); i det här exemplet har jag valt det senare. Observera att den här versionen skiljer sig från Target Android Version och att den alltid bör ställas in på den senast släppta SDK-versionen, eftersom något mindre kommer inte att accepteras i Google Play-butiken.
När du har skapat den här versionen är allt som återstår att importera de nödvändiga NuGet-paketen. För den här handledningen behöver du:
- Xamarin.OpenID.AppAuth.Android – För att autentisera användaren kommer du att använda OpenID Connect-standarden (en förbättring av OAuth 2.0). Det enklaste sättet att implementera klientkod som följer den här specifikationen är att använda AppAuth-klientens SDK för Android, och Xamarin har lämpligen portat ett paket med den här funktionaliteten så att du kan använda den.
- System.IdentityModel.Tokens.Jwt – För autentisering används JSON-webbtokens. För att extrahera de data som behövs från dessa tokens behöver du det här paketet.
Du behöver dessutom de här två paketen eftersom de används av AppAuth:
- Xamarin.Android.Support.v4
- Xamarin.Android.Support.CustomTabs
Förbered dig på Xamarin-projektet
Om du aldrig har arbetat med Android tidigare är en av de viktigaste principerna att sätta sig in i konceptet Activity. Aktiviteter är komponenter som används för att visa ditt användargränssnitt; i sin mest grundläggande form kan du tänka dig att aktiviteter är lika med sidor i en app som användaren kan navigera mellan. I koden representeras en aktivitet av en klass, men precis som en sida i ASP.NET kan du (och kommer nästan alltid att) associera en XML-baserad layoutfil (en .axml-fil) med den för att få en visningsbar design. Alla nya projekt skapar en fil ”MainActivity.cs” och ”activity_main.axml” som den första aktiviteten (dvs. sidan) som körs när programmet öppnas. Detta kan ändras till vilken annan aktivitet som helst genom att använda egenskapen MainLauncher = true
i klassens attribut Activity
.
Resurser är utformade för att hanteras i en egen katalog och följer en något strikt namngivningskonvention. Jag rekommenderar starkt att du lagrar så mycket av dina resurser som möjligt i den här katalogen eftersom det förenklar återanvändningen av dessa variabler för din pågående utveckling. I katalogen ”values” i katalogen Resources hittar du filer med specifika syften:
- Strings.xml – Här finns alla användarinriktade strängar. Den här är särskilt viktig att använda eftersom den gör att du kan lokalisera dina strängar för en global publik.
- Styles.xml – Här hittar du attributen för att styla dina designobjekt; tänk på den som en CSS-fil.
- Colors.xml – Ett ställe där du kan lagra referenser till de färger som du använder mest frekvent som en del av din styling.
- Dimens.xml – Som namnet kanske antyder, där du definierar bestämda dimensioner för appens layout.
De andra anmärkningsvärda katalogerna följer ingen namnkonvention för sina filer, men de måste innehålla vissa filtyper:
- Layout – Plats för lagring av dina .axml-filer. Dessa filer definierar fullständiga aktivitetslayouter, layoutkomponenter som du genererar och fyller på programmatiskt, layouter för dialogrutor (varningsrutor) osv.
- Menu – Här hittar du definitioner av menyer och deras objekt. Detta är .xml-filer som har
menu
som rotelement ochitem
underelement, som kan grupperas medgroup
-element. De vanligaste menyerna du kommer att stöta på är överflödsmenyn (från knappen med de tre vertikala prickarna) eller navigeringsmenyn (från hamburgerknappen på hemsidan). - Mipmap – Här vill du definiera bilder som måste skalas beroende på skärmens täthet, dvs. bilder som refereras till av andra program och som inte används internt. Appens ikon är det vanligaste innehållet som du lägger i mipmap-katalogerna.
- Drawable – Finns inte som standard i en standardprojektmall, men kan skapas själv. Här lagrar du dina bilder/drawable-innehåll som ska användas i appen, t.ex. startskärmen, anpassade mönster för förloppsbalken, ikoner som används i appen osv.
Sist, om du har några råa innehållsfiler som du vill använda som en del av din applikation (t.ex. en text- eller typsnittsfil) är det i Assets-katalogen som du ska placera dem. Som Assets kommer dessa filer att distribueras med din app så att du kan komma åt dem med Asset Manager.
För att lära dig mer om Assets och Resources och hur du använder dem finns det praktiska ”About”-textfiler i varje katalog i ett nyskapat projekt.
Add User Authentication to Xamarin with OpenID Connect
De flesta appar i dessa dagar kräver någon form av användaridentifiering för att utvecklaren ska kunna erbjuda skräddarsydda upplevelser och för att i sin tur göra det möjligt för användaren att behålla sina data på olika enheter/installationer. Utöver detta finns det frågan om åtkomstkontroll som kan vara användbar för att auktorisera en delmängd användare för extra funktionalitet. Naturligtvis kan detta vara en ganska mödosam uppgift, särskilt om du är i branschen för att skapa appar och du behöver ett nytt system för varje enskild app. Tack och lov kan du med Okta konfigurera en applikation på bara några minuter och sedan är allt det hårda arbetet gjort åt dig! Okta-tjänsten är OAuth 2.0-kompatibel och en certifierad OpenID Connect Provider och fungerar därför perfekt med AppAuth-klientens SDK för alla dina autentiserings- och auktoriseringsbehov.
Sätt upp din Okta-applikation
För det första ska du sätta upp en ny applikation i ditt Okta-konto för det här projektet. Om du inte har något ännu är det väldigt enkelt att skapa ett nytt evigt gratis utvecklarkonto.
När det är klart och du har loggat in på utvecklarens instrumentpanel, notera Org-URL:n eftersom vi behöver den senare:
Från instrumentpanelen går du till fliken ”Applications” (program) och därifrån till ”Add Application” (lägg till program). Du skapar en native Android-applikation, så det är bäst att välja plattformsmallen ”Native”.
Från den här sidan lägger du till ett namn för din applikation och lämnar allt annat som standard. När du har sparat noterar du dina URI:er för omdirigering av inloggning och ditt klient-ID eftersom du kommer att behöva dessa härnäst.
För att du enkelt ska kunna använda dessa tre värden från Okta i framtiden rekommenderar jag att du lägger dem i en egen statisk klass i en ny katalog för autentiseringslogiken:
public static class Configuration{ public const string ClientId = "{yourClientId}"; public const string LoginRedirectUri = "{yourRedirectUri}"; public const string OrgUrl = "https://{yourOktaDomain}";}
Skapa autentiseringsleverantören
Låt oss få bort koden. Du måste konfigurera appen för att informera den om ditt URI-schema för omdirigering. Schemat är din URI för omdirigering av inloggning (i standardform för omvänt domännamn) utan sökvägen. Till exempel, från skärmdumpen ovan skulle mitt schema vara ”com.oktapreview.dev-123456”.
Det enklaste sättet att göra detta är att infoga nedanstående avsiktsfilterutdrag i din AndroidManifest.xml
-fil i Properties
-mappen i din lösning. Lägg till följande XML i Application
-taggen och ändra schemavärdet till ditt eget:
<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>
Du måste också definiera modellen för resultatet av din auktorisering med en enkel klass. Även om jag inte kommer att använda alla värden som jag har skrivit nedan i koden, kommer jag att visa hur man fyller dem så att du kan använda dem efteråt. Eftersom detta är en del av din applikations modell skapar du en mapp som heter Models
och lägger till en AuthorizationResult.cs
-klass i den. Lägg sedan till följande kod:
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 har inget globalt tillstånd som du kan arbeta med, så om du vill skicka enkla värden mellan aktiviteter är det bästa sättet att göra detta med Extras
-funktionen på Intent
-objektet. En Intent
är en fördefinierad klass i Android och ett annat centralt begrepp att förstå. Det är abstraktionen av en operation som ska utföras (dvs. dina ”intentioner”), och för att navigera vidare till en annan aktivitet måste du skapa en instans av den här klassen med vilken aktivitet du ”avser” att gå till. Egenskaperna Extras på Intent
-objektet är i själva verket bara ett lexikon med nycklar till objektvärden och nås genom Put
och typade Get
metoder.
Men även om dessa metoder gör användningen relativt tydlig och enkel gillar jag personligen att hålla all åtkomst till dem inom en egen klass (för att vara exakt, en extensions-klass), för att bibehålla en bättre åtskillnad av intressen. Detta är extremt användbart eftersom du inte behöver få tillgång till nycklarna i olika klasser och kan garantera typsäkerhet när du lägger in och hämtar dessa värden. I din auktoriseringsleverantör vill du: lagra AuthState
, kunna kontrollera om den finns där och returnera den om den finns där. Skapa en ny mapp med namnet Extensions
i lösningens rot. Lägg sedan till en ny klass som heter IntentExtensions.cs
. Gör klassen public
och static
och lägg sedan till följande kod inuti klassen:
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 är det dags att definiera auktoriseringsleverantören, AuthorizationProvider.cs
i mappen Authentication
som du skapade tidigare för klassen Configuration.cs
. Först tar du bort alla using
-angivelser inuti den nyskapade klassen och deklarerar sedan konfigurationen som static readonly
-variabler:
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" };
Konfigurationsslutpunkten är en standard i OpenID som upptäcktsslutpunkt för att hitta allt som stöds. Observera att jag här har skrivit att detta använder standardleverantörsnamnet. Om du har en annan leverantör vill du ändra detta här. Observera också att detta använder Android.Net
-varianten av Uri-klassen och inte System
-versionen – du måste lägga till den förstnämnda varianten i dina användningar eller kvalificera typen fullt ut för att detta ska fungera. Variabeln Scopes, liksom alla andra OpenID-system, definierar vad vi är auktoriserade att få tillgång till.
Nästan bör du deklarera dina medlemsvariabler:
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>();
En snabb förklaring av var och en:
- Attestinationsbegäran och den avslutade avsikten är parametrar som skapats för att användas för att göra auktorisationssamtalet. Jag har skrivit dem som globala variabler här för att minimera mängden av överlämnande av parametrar till olika metoder.
- Variabeln authorizationState, som den heter, definierar det aktuella givna auktoriseringstillståndet.
- Variabeln authorizationService innehåller en instans av auktoriseringstjänsten.
- Variabeln context här är den anropande aktivitetens variabel, så att du kan referera till den när det behövs.
- Finally, the taskCompletionSource enables you to make all these calls asynchronously and then return once complete.
Nu bör du definiera värdena för dessa readonly-variabler i din konstruktör och deklarera de offentliga metoder som din kod kommer att anropa:
public AuthorizationProvider(Context context){ authorizationService = new AuthorizationService(context); this.context = context;}public async Task<AuthorizationResult> SignInAsync(){ ...}public void NotifyCallback(Intent intent){ ...}
The SignInAsync method is as you might have guessed an asynchronous method to sign a user in. Den returnerar AuthorizationResult
-klassen som du skrev tidigare. NotifyCallback
å andra sidan är för den anropande aktiviteten, när den har återvänt från den externa inloggningssidan, att ringa tillbaka till auktoriseringsleverantören och låta den veta att den är klar. Sign in-metoden har jag delat upp i flera underrutiner och ser ut så här:
AuthorizationServiceConfiguration serviceConfiguration = await AuthorizationServiceConfiguration.FetchFromUrlAsync(ConfigurationEndpoint);BuildAuthorizationRequest(serviceConfiguration);BuildCompletedIntent(serviceConfiguration);return await RequestAuthorization();
I detta har du definierat tjänstens konfiguration, byggt upp auktorisationsbegäran och intentionen att anropa när auktoriseringen är klar, och sedan inväntar du begäran om auktorisering. Att bygga auktorisationsbegäran är som följer:
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();}
Den här metodens uppgift är att abstrahera bort AuthorizationRequest.Builder
arbetet och skapa begäran. Därefter måste du bygga Intent
för när operationen har slutförts:
private void BuildCompletedIntent(AuthorizationServiceConfiguration serviceConfiguration){ Intent intent = new Intent(context, typeof(MainActivity)); intent.PutAuthStateExtra(new AuthState()); completedIntent = PendingIntent.GetActivity(context, authorizationRequest.GetHashCode(), intent, 0);}
Den ”avsikt” som du vill utföra här är att återvända till din MainActivity
med ett nytt AuthState kopplat. Sist i det här flödet ska du hantera utförandet av begäran:
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 };}
Då PerformAuthorizationRequest
är synkron och returnerar void, väntar koden på taskCompletionSource
-medlemmen, med vetskapen om att den bara kommer att ställas in när ett svar har hämtats. Vid samma tidpunkt vet du att auktoriseringstillståndet kommer att fyllas på (om allting lyckades), och därför kan du returnera deras värden som en del av AuthorizationResult.
Den andra offentliga metoden NotifyCallback
, som jag nämnde tidigare, är det som du vill att MainActivity
-klassen ska ringa tillbaka till, när din ovanstående completedIntent
har körts. I den här metoden vill du verifiera svaret, uppdatera tillståndet på lämpligt sätt och, om det lyckas, utföra en begäran om tokenutbyte:
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);
Här kan du se att jag i misslyckandefallen har ställt in resultatet av taskCompletionSource till false, och det kommer att avblockera RequestAuthorization
-metoden ovan. Dessutom tar PerformTokenRequest
-metoden in en delegat, ReceivedTokenResponse
, som ska köras när den har slutförts. Denna metod ser ut på följande sätt:
private void ReceivedTokenResponse(TokenResponse tokenResponse, AuthorizationException authorizationException){ authorizationState.Update(tokenResponse, authorizationException); taskCompletionSource.SetResult(true);}
I det här läget bör du ha alla auktoriseringsuppgifter du behöver och kan därför uppdatera state på lämpligt sätt (där du hittar värdena som ska returneras från sign in-metoden) och ställa in resultatet för att avblockera taskCompletionSource
-uppgiften.
Implementera autentisering i ditt Xamarin-gränssnitt
Som en upprensning om du vill kan du gärna ta bort alla referenser till ”Floating Action Bar” (även skriven som ”FAB”) i huvudaktivitetens klass/axml-filer eftersom de är onödig bloat i det här skedet.
För att tillåta användaren att logga in måste du nu implementera den här funktionaliteten i användargränssnittet. Med tanke på standarddesignen skulle det mest intuitiva sättet att göra detta vara att lägga till ett objekt i överflödsmenyn i det övre högra hörnet. Du kan göra detta genom att redigera filen menu_main.xml
i mappen ”Resources -> menu” och lägga till det här objektet som ett barn till taggen menu
:
<item android:id="@+id/action_signin" android:orderInCategory="100" android:title="@string/action_signin" app:showAsAction="never" />
Med den här koden har du skapat ett nytt alternativ med en titel som ska definieras i strängresursfilen. Som tidigare nämnts i Android är det bästa tillvägagångssättet att lägga all text som är riktad mot användaren i filen string resources. För att deklarera dessa data redigerar du strings.xml
-filen i mappen ”Resources -> values” och lägger till dessa rader:
<string name="action_signin">Sign In</string><string name="welcome_message_format">Hi, %1$s!</string>
Nu har jag inte bara deklarerat en sträng för knappen ”Sign In” här, utan jag har också ovanför lagt till en sträng för ett välkomstmeddelande till användaren när de har loggat in. Motsvarigheten till C#-koden this string would be
”Hej, {0}!”`, där platshållaren är av typen sträng.
Notera att vid alla uppdateringar av de här resursbaserade filerna kommer klassen Resource.designer.cs automatiskt att återskapas med nya ID:n för varje objekt som du har skapat och som du kan referera till i din kod. Om detta inte fungerar för en viss fil kan du välja den i Solution Explorer och titta på fönstret Egenskaper. Kontrollera att egenskapen CustomTool
är inställd på värdet MSBuild:UpdateGeneratedFiles
, eftersom detta troligen saknas och hindrar designerfilen från att känna igen den som en resurs.
Nästan lägg till en ProgressBar
till den befintliga activity_main.axml
layoutfilen:
<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" />
Den här ProgressBar
(eller spinnern, beroende på vad som är fallet) har ett ID som du kan referera till i koden, och den är inställd så att den ska sitta runt mitten av skärmen. Synligheten är inställd på borta för tillfället, men när din auktoriseringskod körs kan du ställa in den på synlig och informera användaren om att appen är upptagen.
Nu har du en knapp för att öppna autentisering och en spinnare som informerar användaren om att appen är upptagen, det är dags att använda dem. I din MainActivity
-klass lägger du till följande egenskap till Activity
-attributet (ovanför klassrubriken):
LaunchMode = LaunchMode.SingleTask
Denna egenskap säkerställer att det bara finns en instans av MainActivity
-klassen och att du inte fortsätter att öppna nya. När du har gjort detta lägger du till en statisk medlemsvariabel för AuthorizationProvder
som du skrev ovan och skapar en instans av den i den befintliga överstyrningen av OnCreate
-metoden. Observera att detta ska göras efter den befintliga koden inom metoden:
private static AuthorizationProvider authorizationProvider;protected override void OnCreate(Bundle savedInstanceState){ ... authorizationProvider = new AuthorizationProvider(this);}
Nästan, åsidosätt OnNewIntent
-metoden. Syftet med detta är att när en ny avsikt av den här klassen skapas (dvs. när det externa inloggningsfönstret återkommer), anropar du NotifyCallback
-metoden från AuthorizationProvider
. I detta ingår också en snabb kontroll för att se att det är det förväntade flödet:
protected override void OnNewIntent(Intent intent){ base.OnNewIntent(intent); if (intent != null && intent.Data.Path.Equals("/callback", StringComparison.OrdinalIgnoreCase)) { authorizationProvider.NotifyCallback(intent); }}
Nu lägger du till koden bakom menyalternativet som du lagt till. I den befintliga överstyrningen av metoden OnOptionsItemSelected
lägger du till ett if
-uttalande med ett anrop till en ny metod som hanterar inloggningsprocessen på följande sätt:
if (id == Resource.Id.action_signin){ OnSignInAttempted(); return true;}
Denna nya metod kommer att börja med att göra ProgressBar
som du lade till för en stund sedan synlig; för att hämta vilken komponent som helst från layoutfilen, använd den generiska metoden FindViewById
och ange komponentens ID som argument. Därefter gör du ett anrop till metoden SignInAsync
och väntar på resultatet. När anropet har återkommit verifieras resultatet sedan som auktoriserat. Om denna auktorisering misslyckades av någon anledning visas en feldialog och framstegsskivan försvinner igen. Jag lämnar detaljerna för framgångsfallet för tillfället eftersom du fortfarande behöver någonstans att gå i det fallet:
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; }}
När användaren är autentiserad bör du omdirigera honom eller henne till nästa sida i din upplevelse. Om du minns tidigare så representeras varje sida av en aktivitet, så du måste skapa en ny nu.
För att börja måste du i mappen ”Resources -> layout” skapa den nya aktivitetslayoutfilen ”activity_dashboard.axml”. Det enklaste sättet att göra detta är att gå till alternativet New Item… i kontextmenyn och välja mallen ”Android Layout”. I din nya layoutfil lägger du till en enkel TextView-komponent för att visa text så här:
<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>
I det här utdraget har du en TextView
-komponent med ett refererbart ID som är centrerad i mitten av sidan och som kommer att visa ett välkomstmeddelande. Skapa sedan en motsvarande aktivitetsklass ”DashboardActivity” med hjälp av alternativet ”New Item…” i kontextmenyn från projektet i lösningsutforskaren och välj mallen ”Activity”. För att koppla den här klassen till sin layoutfil måste du anropa funktionen SetContentView i den genererade OnCreate()
-metoden (under den nedärvda basmetodens invocation):
SetContentView(Resource.Layout.activity_dashboard);
För att anpassa ditt välkomstmeddelande vill du skicka användarens namn till din nya aktivitet. Om du minns tidigare var det bästa sättet att göra detta med Extras on the intent, och du skapade en tilläggsklass för att hantera detta. Precis som tidigare lägger du till nya metoder för ”Put” och ”Get” av ett ”name”-extra i IntentExtensions.cs
-filen som du skapade ovan:
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);}
Med hjälp av den här utökade funktionaliteten kan du nu, efter anropet till SetContentView
som du gjorde i OnCreate()
-metoden, hämta användarens namn och ställa in TextView
-komponentens text på lämpligt sätt:
string name = Intent.GetNameExtra();TextView welcomeMessage = FindViewById<TextView>(Resource.Id.welcome_message);welcomeMessage.Text = Resources.GetString(Resource.String.welcome_message_format, name);
I det här utdraget sätts värdet av TextView
-instansen vid hämtning till ditt välkomstmeddelande, som skapas med hjälp av Android Resources motsvarighet till string.Format()
.
Med detta är din instrumentbrädaktivitet klar och du måste nu anropa den. I platshållaren för det framgångsfall som jag lämnade öppet från OnSignInAttempted
-metoden kan du uppnå detta genom att lägga till följande kod:
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();
Det första blocket läser tokenet och hämtar användarens namn (om det finns). I det andra skapas en ny Intent
för instrumentbrädaktiviteten, användarens namn lagras i denna Intent
med hjälp av din ovan definierade utvidgningsmetod, och sedan startas aktiviteten (dvs. man navigerar till den). För att förhindra att användaren navigerar tillbaka till den här sidan efteråt avslutas koden med att anropa Finish()
-metoden.
Kör din Android-applikation
Nu är det dags att starta applikationen med den valda enheten!
Om du felsöker med hjälp av emulatorn bör det vara lika enkelt som att trycka på F5 av vilket först öppnar och laddar emulatorn, och sedan distribueras koden till den. Som en sidobemärkning behöver du inte stänga emulatorn mellan kör-/felsökningsförsöken, eftersom den bara behöver omdistribuera appen.
Om du felsöker med hjälp av en enhet som inte har använts för det här ändamålet tidigare, måste du konfigurera enheten först. Gör detta genom att aktivera utvecklaralternativ och i den nya menyn slå på ”Android debugging” under rubriken ”Debugging”. Efter detta är det bara att koppla in enheten, acceptera dialogrutan på enheten som bekräftar att detta är en säker debugging-anslutning och du bör kunna distribuera och köra din app med F5. Observera att fysiska enheter har högre prioritet än emulatorn och kommer att byta till den som standardalternativ för felsökning när de är anslutna.
När din app har distribuerats och laddats välkomnas du av standardmallen för en enda sida. Öppna menyn i det övre högra hörnet för att logga in, och när du har angett dina uppgifter bör du återvända till den här sidan med den snurrande förloppsindikatorn innan du automatiskt skickas till instrumentbrädsidan med ditt välkomstmeddelande:
Lär dig mer om Xamarin, OpenID Connect och säker autentisering
Om du har följt med i alla de här stegen har du nu en grundläggande Android-app som byggts med Xamarin.Android, med fullt fungerande användarautentisering baserad på OpenID och Okta-tjänsten. Härifrån kan du enkelt utöka dashboardaktiviteten för att implementera din funktionalitet.
För att se koden från det här inlägget i sin helhet går du över till vår GitHub-sida.
Om den här handledningen har väckt din aptit på Xamarin-utveckling och du vill lära dig mer, rekommenderar jag starkt att du tar en titt på dessa andra bra artiklar:
- Add Identity Management to Your Android App
- Add Authentication to Your Xamarin.Forms App with OpenID Connect
- Build an App for iOS and Android with Xamarin