├── .github ├── FUNDING.yml └── workflows │ ├── publish-release.yml │ ├── pr.yml │ ├── start-release.yml │ └── ci.yml ├── avantipoint-icon.png ├── sample ├── DemoMobileApp │ ├── Resources │ │ ├── Fonts │ │ │ ├── OpenSans-Regular.ttf │ │ │ └── OpenSans-Semibold.ttf │ │ ├── AppIcon │ │ │ ├── appicon.svg │ │ │ └── appiconfg.svg │ │ ├── Raw │ │ │ └── AboutAssets.txt │ │ ├── Splash │ │ │ └── splash.svg │ │ ├── Styles │ │ │ ├── Colors.xaml │ │ │ └── Styles.xaml │ │ └── Images │ │ │ └── dotnet_bot.svg │ ├── AppShell.xaml.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Platforms │ │ ├── iOS │ │ │ ├── AppDelegate.cs │ │ │ ├── Program.cs │ │ │ └── Info.plist │ │ ├── Android │ │ │ ├── Resources │ │ │ │ └── values │ │ │ │ │ └── colors.xml │ │ │ ├── AndroidManifest.xml │ │ │ ├── MainApplication.cs │ │ │ ├── MainActivity.cs │ │ │ └── WebAuthenticatorActivity.cs │ │ ├── MacCatalyst │ │ │ ├── AppDelegate.cs │ │ │ ├── Program.cs │ │ │ └── Info.plist │ │ ├── Windows │ │ │ ├── App.xaml │ │ │ ├── app.manifest │ │ │ ├── App.xaml.cs │ │ │ └── Package.appxmanifest │ │ └── Tizen │ │ │ ├── Main.cs │ │ │ └── tizen-manifest.xml │ ├── MainPage.xaml.cs │ ├── Services │ │ └── IUserProfileService.cs │ ├── AppShell.xaml │ ├── Converters │ │ └── IsAuthenticatedConverter.cs │ ├── MauiProgram.cs │ ├── ViewModels │ │ └── MainPageViewModel.cs │ ├── MainPage.xaml │ └── DemoMobileApp.csproj └── DemoAPI │ ├── Data │ ├── AuthorizedTokens.cs │ ├── UserRole.cs │ └── UserContext.cs │ ├── web.config │ ├── appsettings.json │ ├── DemoAPI.csproj │ ├── Properties │ └── launchSettings.json │ ├── CustomClaimsHandler.cs │ └── Program.cs ├── src └── AvantiPoint.MobileAuth │ ├── build │ └── AvantiPoint.MobileAuth.props │ ├── Authentication │ ├── ITokenOptions.cs │ ├── ITokenService.cs │ ├── AuthenticationExtensions.cs │ ├── MobileJwtValidationHandler.cs │ └── TokenService.cs │ ├── Stores │ ├── GeneratedToken.cs │ ├── ITokenStore.cs │ └── TokenStore.cs │ ├── IMobileAuthClaimsHandler.cs │ ├── Configuration │ ├── OAuthProviderOptions.cs │ ├── GoogleProviderOptions.cs │ ├── MicrosoftProviderOptions.cs │ ├── OAuthLibraryOptions.cs │ └── AppleOAuthOptions.cs │ ├── Http │ └── HttpContextHelpers.cs │ ├── AvantiPoint.MobileAuth.csproj │ ├── MobileAuthenticationBuilder.cs │ ├── MobileAuthClaimsHandler.cs │ └── MobileAuth.cs ├── version.json ├── LICENSE ├── Directory.Build.props ├── AvantiPoint.MobileAuth.sln ├── ReadMe.md └── .gitignore /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [dansiegel] 2 | custom: ["https://www.paypal.me/dansiegel"] -------------------------------------------------------------------------------- /avantipoint-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvantiPoint/mobileauth-lib/HEAD/avantipoint-icon.png -------------------------------------------------------------------------------- /sample/DemoMobileApp/Resources/Fonts/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvantiPoint/mobileauth-lib/HEAD/sample/DemoMobileApp/Resources/Fonts/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /sample/DemoMobileApp/Resources/Fonts/OpenSans-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AvantiPoint/mobileauth-lib/HEAD/sample/DemoMobileApp/Resources/Fonts/OpenSans-Semibold.ttf -------------------------------------------------------------------------------- /sample/DemoMobileApp/AppShell.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace DemoMobileApp; 2 | 3 | public partial class AppShell : Shell 4 | { 5 | public AppShell() 6 | { 7 | InitializeComponent(); 8 | } 9 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Windows Machine": { 4 | "commandName": "MsixPackage", 5 | "nativeDebugging": false 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /sample/DemoAPI/Data/AuthorizedTokens.cs: -------------------------------------------------------------------------------- 1 | namespace DemoAPI.Data; 2 | 3 | public class AuthorizedTokens 4 | { 5 | public Guid Id { get; set; } 6 | 7 | public string Token { get; set; } = default!; 8 | } 9 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/build/AvantiPoint.MobileAuth.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Authentication/ITokenOptions.cs: -------------------------------------------------------------------------------- 1 | namespace AvantiPoint.MobileAuth.Authentication; 2 | 3 | public interface ITokenOptions 4 | { 5 | string? JwtKey { get; } 6 | bool OverrideTokenExpiration { get; } 7 | TimeSpan DefaultExpiration { get; } 8 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Foundation; 2 | 3 | namespace DemoMobileApp; 4 | 5 | [Register("AppDelegate")] 6 | public class AppDelegate : MauiUIApplicationDelegate 7 | { 8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 9 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Android/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #512BD4 4 | #2B0B98 5 | #2B0B98 6 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Resources/AppIcon/appicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Stores/GeneratedToken.cs: -------------------------------------------------------------------------------- 1 | namespace AvantiPoint.MobileAuth.Stores; 2 | 3 | public class GeneratedToken 4 | { 5 | public string Token { get; set; } = default!; 6 | public DateTimeOffset Expires { get; set; } 7 | public bool Revoked { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/MacCatalyst/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Foundation; 2 | 3 | namespace DemoMobileApp; 4 | 5 | [Register("AppDelegate")] 6 | public class AppDelegate : MauiUIApplicationDelegate 7 | { 8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 9 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/MainPage.xaml.cs: -------------------------------------------------------------------------------- 1 | using DemoMobileApp.ViewModels; 2 | 3 | namespace DemoMobileApp; 4 | 5 | public partial class MainPage : ContentPage 6 | { 7 | public MainPage(MainPageViewModel viewModel) 8 | { 9 | InitializeComponent(); 10 | BindingContext = viewModel; 11 | } 12 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Services/IUserProfileService.cs: -------------------------------------------------------------------------------- 1 | using Refit; 2 | 3 | namespace DemoMobileApp.Services; 4 | 5 | public interface IUserProfileService 6 | { 7 | [Headers("Authorization: Bearer")] 8 | [Get("/profile")] 9 | Task>> GetProfileClaims(); 10 | } 11 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", 3 | "version": "0.2", 4 | "assemblyVersion": { 5 | "precision": "revision" 6 | }, 7 | "publicReleaseRefSpec": [ 8 | "^refs/heads/master$" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish MobileAuth Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish-release: 9 | uses: avantipoint/workflow-templates/.github/workflows/deploy-nuget-from-release.yml@master 10 | secrets: 11 | apiKey: ${{ secrets.NUGET_API_KEY }} 12 | -------------------------------------------------------------------------------- /sample/DemoAPI/Data/UserRole.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace DemoAPI.Data; 4 | 5 | public class UserRole 6 | { 7 | public Guid Id { get; set; } 8 | 9 | [EmailAddress] 10 | public string Email { get; set; } = default!; 11 | public string Role { get; set; } = default!; 12 | } 13 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Stores/ITokenStore.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace AvantiPoint.MobileAuth.Stores; 4 | 5 | public interface ITokenStore 6 | { 7 | ValueTask AddToken(string jwt, DateTimeOffset expires); 8 | ValueTask TokenExists(string jwt); 9 | ValueTask RemoveToken(string jwt); 10 | } 11 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Windows/App.xaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sample/DemoAPI/web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/IMobileAuthClaimsHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Microsoft.AspNetCore.Authentication; 3 | using Microsoft.AspNetCore.Http; 4 | 5 | namespace AvantiPoint.MobileAuth; 6 | 7 | public interface IMobileAuthClaimsHandler 8 | { 9 | ValueTask> GenerateClaims(HttpContext context, AuthenticateResult auth, string scheme); 10 | } 11 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Configuration/OAuthProviderOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | 3 | namespace AvantiPoint.MobileAuth.Configuration; 4 | 5 | internal abstract class OAuthProviderOptions 6 | { 7 | public string? ClientId { get; set; } 8 | 9 | public string? ClientSecret { get; set; } 10 | 11 | public abstract void Configure(AuthenticationBuilder builder); 12 | } 13 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Tizen/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Maui; 3 | using Microsoft.Maui.Hosting; 4 | 5 | namespace DemoMobileApp; 6 | 7 | internal class Program : MauiApplication 8 | { 9 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 10 | 11 | static void Main(string[] args) 12 | { 13 | var app = new Program(); 14 | app.Run(args); 15 | } 16 | } -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Authentication/ITokenService.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using Microsoft.IdentityModel.Tokens; 3 | 4 | namespace AvantiPoint.MobileAuth.Authentication; 5 | 6 | public interface ITokenService 7 | { 8 | ValueTask BuildToken(IEnumerable claims); 9 | ValueTask IsTokenValid(string token); 10 | ValueTask InvalidateToken(string token); 11 | SymmetricSecurityKey GetKey(); 12 | } 13 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/iOS/Program.cs: -------------------------------------------------------------------------------- 1 | using ObjCRuntime; 2 | using UIKit; 3 | 4 | namespace DemoMobileApp; 5 | 6 | public class Program 7 | { 8 | // This is the main entry point of the application. 9 | static void Main(string[] args) 10 | { 11 | // if you want to use a different Application Delegate class from "AppDelegate" 12 | // you can specify it here. 13 | UIApplication.Main(args, null, typeof(AppDelegate)); 14 | } 15 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/MacCatalyst/Program.cs: -------------------------------------------------------------------------------- 1 | using ObjCRuntime; 2 | using UIKit; 3 | 4 | namespace DemoMobileApp; 5 | 6 | public class Program 7 | { 8 | // This is the main entry point of the application. 9 | static void Main(string[] args) 10 | { 11 | // if you want to use a different Application Delegate class from "AppDelegate" 12 | // you can specify it here. 13 | UIApplication.Main(args, null, typeof(AppDelegate)); 14 | } 15 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Android/MainApplication.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Runtime; 3 | 4 | namespace DemoMobileApp 5 | { 6 | [Application] 7 | public class MainApplication : MauiApplication 8 | { 9 | public MainApplication(IntPtr handle, JniHandleOwnership ownership) 10 | : base(handle, ownership) 11 | { 12 | } 13 | 14 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 15 | } 16 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Android/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content.PM; 3 | using Android.OS; 4 | 5 | namespace DemoMobileApp; 6 | 7 | [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] 8 | public class MainActivity : MauiAppCompatActivity 9 | { 10 | } 11 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/AppShell.xaml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Authentication/AuthenticationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | 3 | namespace AvantiPoint.MobileAuth.Authentication; 4 | 5 | internal static class AuthenticationExtensions 6 | { 7 | public static bool ContainsKey(this IEnumerable claims, string type) => 8 | claims.Any(x => x.Type == type); 9 | 10 | public static string? FindFirstValue(this IEnumerable claims, string type) => 11 | claims.FirstOrDefault(x => x.Type == type)?.Value; 12 | } 13 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Android/WebAuthenticatorActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content; 3 | using Android.Content.PM; 4 | 5 | namespace DemoMobileApp; 6 | 7 | [Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)] 8 | [IntentFilter(new[] { Intent.ActionView }, 9 | Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable }, 10 | DataScheme = Constants.CallbackScheme)] 11 | public class WebAuthenticatorActivity : WebAuthenticatorCallbackActivity 12 | { 13 | } -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Mobile Auth PR Validation 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | paths: 7 | - "*.props" 8 | - "sample/**" 9 | - "src/**" 10 | - "version.json" 11 | - ".github/workflows/ci.yml" 12 | 13 | jobs: 14 | build: 15 | uses: avantipoint/workflow-templates/.github/workflows/dotnet-build.yml@master 16 | permissions: 17 | statuses: write 18 | checks: write 19 | with: 20 | name: Mobile Auth 21 | solution-path: build.slnf 22 | run-tests: false -------------------------------------------------------------------------------- /sample/DemoAPI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "OAuth": { 10 | "CallbackScheme": "myapp-scheme", 11 | "JwtKey": "DanSiegelMakesTheBestLibraries", 12 | "Apple": { 13 | "ServiceId": "", 14 | "KeyId": "", 15 | "TeamId": "" 16 | }, 17 | "Google": { 18 | "ClientId": "", 19 | "ClientSecret": "" 20 | }, 21 | "Microsoft": { 22 | "ClientId": "", 23 | "ClientSecret": "" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/DemoAPI/DemoAPI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Configuration/GoogleProviderOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace AvantiPoint.MobileAuth.Configuration; 5 | 6 | internal sealed class GoogleProviderOptions : OAuthProviderOptions 7 | { 8 | public override void Configure(AuthenticationBuilder builder) 9 | { 10 | if (string.IsNullOrEmpty(ClientId) || string.IsNullOrEmpty(ClientSecret)) 11 | return; 12 | 13 | builder.AddGoogle(options => 14 | { 15 | options.ClientId = ClientId; 16 | options.ClientSecret = ClientSecret; 17 | options.SaveTokens = true; 18 | }); 19 | } 20 | } -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Configuration/MicrosoftProviderOptions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace AvantiPoint.MobileAuth.Configuration; 5 | 6 | internal sealed class MicrosoftProviderOptions : OAuthProviderOptions 7 | { 8 | public override void Configure(AuthenticationBuilder builder) 9 | { 10 | if (string.IsNullOrEmpty(ClientId) || string.IsNullOrEmpty(ClientSecret)) 11 | return; 12 | 13 | builder.AddMicrosoftAccount(options => 14 | { 15 | options.ClientId = ClientId; 16 | options.ClientSecret = ClientSecret; 17 | options.SaveTokens = true; 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Resources/Raw/AboutAssets.txt: -------------------------------------------------------------------------------- 1 | Any raw assets you want to be deployed with your application can be placed in 2 | this directory (and child directories). Deployment of the asset to your application 3 | is automatically handled by the following `MauiAsset` Build Action within your `.csproj`. 4 | 5 | 6 | 7 | These files will be deployed with you package and will be accessible using Essentials: 8 | 9 | async Task LoadMauiAsset() 10 | { 11 | using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); 12 | using var reader = new StreamReader(stream); 13 | 14 | var contents = reader.ReadToEnd(); 15 | } 16 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Converters/IsAuthenticatedConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace DemoMobileApp.Converters; 4 | 5 | public class IsAuthenticatedConverter : IValueConverter 6 | { 7 | public bool Invert { get; set; } 8 | 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | if (value is not IEnumerable claims) 12 | return GetValue(false); 13 | 14 | return GetValue(claims.Any()); 15 | } 16 | 17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | 22 | private bool GetValue(bool value) => 23 | Invert ? !value : value; 24 | } 25 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Tizen/tizen-manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | appicon.xhigh.png 7 | 8 | 9 | 10 | 11 | http://tizen.org/privilege/internet 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Http/HttpContextHelpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | 3 | namespace AvantiPoint.MobileAuth.Http; 4 | 5 | internal static class HttpContextHelpers 6 | { 7 | public static Task StatusCode(this HttpContext context, int statusCode) 8 | { 9 | context.Response.StatusCode = statusCode; 10 | return context.Response.Body.FlushAsync(); 11 | } 12 | 13 | public static Task Ok(this HttpContext context) 14 | => context.StatusCode(StatusCodes.Status200OK); 15 | 16 | public static Task BadRequest(this HttpContext context) 17 | => context.StatusCode(StatusCodes.Status400BadRequest); 18 | 19 | public static Task NoContent(this HttpContext context) 20 | => context.StatusCode(StatusCodes.Status204NoContent); 21 | } 22 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Windows/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | true/PM 12 | PerMonitorV2, PerMonitor 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Windows/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.UI.Xaml; 2 | 3 | // To learn more about WinUI, the WinUI project structure, 4 | // and more about our project templates, see: http://aka.ms/winui-project-info. 5 | 6 | namespace DemoMobileApp.WinUI; 7 | 8 | /// 9 | /// Provides application-specific behavior to supplement the default Application class. 10 | /// 11 | public partial class App : MauiWinUIApplication 12 | { 13 | /// 14 | /// Initializes the singleton application object. This is the first line of authored code 15 | /// executed, and as such is the logical equivalent of main() or WinMain(). 16 | /// 17 | public App() 18 | { 19 | this.InitializeComponent(); 20 | } 21 | 22 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); 23 | } -------------------------------------------------------------------------------- /sample/DemoAPI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:38193", 8 | "sslPort": 44357 9 | } 10 | }, 11 | "profiles": { 12 | "DemoAPI": { 13 | "commandName": "Project", 14 | "dotnetRunMessages": true, 15 | "launchBrowser": true, 16 | "launchUrl": "swagger", 17 | "applicationUrl": "https://localhost:7172;http://localhost:5172", 18 | "environmentVariables": { 19 | "ASPNETCORE_ENVIRONMENT": "Development" 20 | } 21 | }, 22 | "IIS Express": { 23 | "commandName": "IISExpress", 24 | "launchBrowser": true, 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/start-release.yml: -------------------------------------------------------------------------------- 1 | name: Start NuGet Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build: 8 | uses: avantipoint/workflow-templates/.github/workflows/dotnet-build.yml@master 9 | permissions: 10 | statuses: write 11 | checks: write 12 | with: 13 | name: Mobile Auth 14 | solution-path: build.slnf 15 | run-tests: false 16 | code-sign: true 17 | secrets: 18 | codeSignKeyVault: ${{ secrets.CodeSignKeyVault }} 19 | codeSignClientId: ${{ secrets.CodeSignClientId }} 20 | codeSignTenantId: ${{ secrets.CodeSignTenantId }} 21 | codeSignClientSecret: ${{ secrets.CodeSignClientSecret }} 22 | codeSignCertificate: ${{ secrets.CodeSignCertificate }} 23 | 24 | release: 25 | uses: avantipoint/workflow-templates/.github/workflows/generate-release.yml@master 26 | needs: [build] 27 | permissions: 28 | contents: write 29 | with: 30 | package-name: AvantiPoint.MobileAuth 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 AvantiPoint, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sample/DemoAPI/Data/UserContext.cs: -------------------------------------------------------------------------------- 1 | using AvantiPoint.MobileAuth.Stores; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace DemoAPI.Data; 5 | 6 | public class UserContext : DbContext, ITokenStore 7 | { 8 | public UserContext(DbContextOptions options) 9 | : base(options) 10 | { 11 | } 12 | 13 | public DbSet AuthorizedTokens { get; set; } 14 | 15 | public DbSet UserRoles { get; set; } 16 | 17 | public async ValueTask AddToken(string jwt, DateTimeOffset expires) 18 | { 19 | await AuthorizedTokens.AddAsync(new Data.AuthorizedTokens() 20 | { 21 | Token = jwt 22 | }); 23 | await SaveChangesAsync(); 24 | } 25 | 26 | public async ValueTask RemoveToken(string jwt) 27 | { 28 | var tokens = await AuthorizedTokens.Where(x => x.Token == jwt).ToArrayAsync(); 29 | if(tokens.Any()) 30 | { 31 | AuthorizedTokens.RemoveRange(tokens); 32 | await SaveChangesAsync(); 33 | } 34 | } 35 | 36 | public async ValueTask TokenExists(string jwt) => 37 | await AuthorizedTokens.AnyAsync(x => x.Token == jwt); 38 | } 39 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Stores/TokenStore.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.VisualBasic; 3 | 4 | namespace AvantiPoint.MobileAuth.Stores; 5 | 6 | internal class TokenStore : DbContext, ITokenStore 7 | { 8 | public TokenStore(DbContextOptions options) 9 | : base(options) 10 | { 11 | Tokens = Set(); 12 | } 13 | 14 | public DbSet Tokens { get; } 15 | 16 | public async ValueTask AddToken(string jwt, DateTimeOffset expires) 17 | { 18 | await Tokens.AddAsync(new GeneratedToken 19 | { 20 | Token = jwt, 21 | Expires = expires 22 | }); 23 | await SaveChangesAsync(); 24 | } 25 | 26 | public async ValueTask RemoveToken(string jwt) 27 | { 28 | var tokens = await Tokens.Where(x => x.Token == jwt).ToArrayAsync(); 29 | if(tokens.Any()) 30 | { 31 | Tokens.RemoveRange(tokens); 32 | await SaveChangesAsync(); 33 | } 34 | } 35 | 36 | public async ValueTask TokenExists(string jwt) => 37 | await Tokens.AnyAsync(x => x.Token == jwt && x.Expires > DateTimeOffset.Now); 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Mobile Auth CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - "*.props" 8 | - "sample/**" 9 | - "src/**" 10 | - "version.json" 11 | - ".github/workflows/ci.yml" 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | uses: avantipoint/workflow-templates/.github/workflows/dotnet-build.yml@master 17 | permissions: 18 | statuses: write 19 | checks: write 20 | with: 21 | name: Mobile Auth 22 | solution-path: build.slnf 23 | run-tests: false 24 | code-sign: true 25 | secrets: 26 | codeSignKeyVault: ${{ secrets.CodeSignKeyVault }} 27 | codeSignClientId: ${{ secrets.CodeSignClientId }} 28 | codeSignTenantId: ${{ secrets.CodeSignTenantId }} 29 | codeSignClientSecret: ${{ secrets.CodeSignClientSecret }} 30 | codeSignCertificate: ${{ secrets.CodeSignCertificate }} 31 | 32 | deploy-internal: 33 | uses: avantipoint/workflow-templates/.github/workflows/deploy-nuget.yml@master 34 | needs: build 35 | with: 36 | name: Deploy Internal 37 | secrets: 38 | feedUrl: ${{ secrets.IN_HOUSE_NUGET_FEED }} 39 | apiKey: ${{ secrets.IN_HOUSE_API_KEY }} -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Configuration/OAuthLibraryOptions.cs: -------------------------------------------------------------------------------- 1 | using AvantiPoint.MobileAuth.Authentication; 2 | 3 | namespace AvantiPoint.MobileAuth.Configuration; 4 | 5 | internal class OAuthLibraryOptions : ITokenOptions 6 | { 7 | public string? AuthPath { get; set; } 8 | 9 | public string? CallbackScheme { get; set; } 10 | 11 | public string? JwtKey { get; set; } 12 | 13 | public TimeSpan DefaultExpiration { get; set; } = TimeSpan.FromMinutes(30); 14 | 15 | public bool OverrideTokenExpiration { get; set; } 16 | 17 | public AppleOAuthOptions? Apple { get; set; } 18 | 19 | public GoogleProviderOptions? Google { get; set; } 20 | 21 | public MicrosoftProviderOptions? Microsoft { get; set; } 22 | 23 | internal string Signin => $"/{SanitizePath()}/signin"; 24 | 25 | internal string Signout => $"/{SanitizePath()}/signout"; 26 | 27 | internal string Refresh => $"/{SanitizePath()}/refresh"; 28 | 29 | private string SanitizePath() 30 | { 31 | var path = AuthPath; 32 | if (!string.IsNullOrEmpty(path)) 33 | { 34 | if (path.EndsWith('/')) 35 | path = path.Substring(0, path.Length - 1); 36 | 37 | if (path.StartsWith('/')) 38 | path = path.Substring(1); 39 | } 40 | 41 | if (string.IsNullOrEmpty(path)) 42 | path = "mobileauth"; 43 | 44 | return path; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/MacCatalyst/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIDeviceFamily 6 | 7 | 1 8 | 2 9 | 10 | UIRequiredDeviceCapabilities 11 | 12 | arm64 13 | 14 | UISupportedInterfaceOrientations 15 | 16 | UIInterfaceOrientationPortrait 17 | UIInterfaceOrientationLandscapeLeft 18 | UIInterfaceOrientationLandscapeRight 19 | 20 | UISupportedInterfaceOrientations~ipad 21 | 22 | UIInterfaceOrientationPortrait 23 | UIInterfaceOrientationPortraitUpsideDown 24 | UIInterfaceOrientationLandscapeLeft 25 | UIInterfaceOrientationLandscapeRight 26 | 27 | XSAppIconAssets 28 | Assets.xcassets/appicon.appiconset 29 | CFBundleURLTypes 30 | 31 | 32 | CFBundleURLName 33 | DemoMobileApp 34 | CFBundleURLSchemes 35 | 36 | myapp-scheme 37 | 38 | CFBundleTypeRole 39 | Editor 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSRequiresIPhoneOS 6 | 7 | UIDeviceFamily 8 | 9 | 1 10 | 2 11 | 12 | UIRequiredDeviceCapabilities 13 | 14 | arm64 15 | 16 | UISupportedInterfaceOrientations 17 | 18 | UIInterfaceOrientationPortrait 19 | UIInterfaceOrientationLandscapeLeft 20 | UIInterfaceOrientationLandscapeRight 21 | 22 | UISupportedInterfaceOrientations~ipad 23 | 24 | UIInterfaceOrientationPortrait 25 | UIInterfaceOrientationPortraitUpsideDown 26 | UIInterfaceOrientationLandscapeLeft 27 | UIInterfaceOrientationLandscapeRight 28 | 29 | XSAppIconAssets 30 | Assets.xcassets/appicon.appiconset 31 | CFBundleURLTypes 32 | 33 | 34 | CFBundleURLName 35 | DemoMobileApp 36 | CFBundleURLSchemes 37 | 38 | myapp-scheme 39 | 40 | CFBundleTypeRole 41 | Editor 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /sample/DemoAPI/CustomClaimsHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Claims; 2 | using AvantiPoint.MobileAuth; 3 | using DemoAPI.Data; 4 | using Microsoft.AspNetCore.Authentication; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | // If needed provide this as a Scoped Service. 8 | public class CustomClaimsHandler : MobileAuthClaimsHandler 9 | { 10 | private UserContext _userContext { get; } 11 | public CustomClaimsHandler(UserContext userContext) 12 | { 13 | _userContext = userContext; 14 | } 15 | 16 | public override async ValueTask> GenerateClaims(HttpContext context, AuthenticateResult auth, string scheme) 17 | { 18 | var claims = (await base.GenerateClaims(context, auth, scheme).ConfigureAwait(false)).ToList(); 19 | var email = FindFirstValue(claims, "email"); 20 | if (string.IsNullOrEmpty(email)) 21 | throw new InvalidOperationException("The claims do not contain an email claim"); 22 | 23 | // Need to update a database or specify specific claims? You can do that here... 24 | var userRoles = await _userContext.UserRoles.Where(x => x.Email == email).ToArrayAsync().ConfigureAwait(false); 25 | if(!userRoles.Any()) 26 | { 27 | userRoles = new UserRole[] { new UserRole { Email = email, Role = "GenericUser" } }; 28 | await _userContext.UserRoles.AddRangeAsync(userRoles).ConfigureAwait(false); 29 | await _userContext.SaveChangesAsync().ConfigureAwait(false); 30 | } 31 | 32 | claims.AddRange(userRoles.Select(x => new Claim(ClaimTypes.Role, x.Role))); 33 | 34 | return claims; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/Authentication/MobileJwtValidationHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Encodings.Web; 2 | using System.Text.RegularExpressions; 3 | using Microsoft.AspNetCore.Authentication; 4 | using Microsoft.AspNetCore.Authentication.JwtBearer; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.Extensions.Options; 7 | 8 | namespace AvantiPoint.MobileAuth.Authentication; 9 | 10 | internal sealed class MobileJwtValidationHandler : JwtBearerHandler 11 | { 12 | private ITokenService _tokenService { get; } 13 | 14 | public MobileJwtValidationHandler( 15 | IOptionsMonitor options, 16 | ILoggerFactory logger, 17 | UrlEncoder encoder, 18 | ITokenService tokenService, 19 | ISystemClock clock) 20 | : base(options, logger, encoder, clock) 21 | { 22 | _tokenService = tokenService; 23 | } 24 | 25 | protected override async Task HandleAuthenticateAsync() 26 | { 27 | var host = $"{Request.Scheme}://{Request.Host.Value}"; 28 | Options.TokenValidationParameters.ValidAudience = host; 29 | Options.TokenValidationParameters.ValidIssuer = host; 30 | Options.TokenValidationParameters.IssuerSigningKey = _tokenService.GetKey(); 31 | 32 | string? header = Request.Headers.Authorization; 33 | if (string.IsNullOrEmpty(header)) 34 | return AuthenticateResult.NoResult(); 35 | 36 | var token = Regex.Replace(header, "Bearer", string.Empty).Trim(); 37 | if (string.IsNullOrEmpty(token)) 38 | return AuthenticateResult.NoResult(); 39 | else if (!await _tokenService.IsTokenValid(token)) 40 | return AuthenticateResult.Fail("No valid token found"); 41 | 42 | return await base.HandleAuthenticateAsync(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Resources/Splash/splash.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Resources/AppIcon/appiconfg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/MauiProgram.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Maui; 2 | using DemoMobileApp.Services; 3 | using DemoMobileApp.ViewModels; 4 | using Refit; 5 | 6 | namespace DemoMobileApp; 7 | 8 | public static class Constants 9 | { 10 | public const string BaseUrl = "https://localhost:7172"; 11 | public const string CallbackScheme = "myapp-scheme"; 12 | } 13 | 14 | public static class MauiProgram 15 | { 16 | public static MauiApp CreateMauiApp() 17 | { 18 | var builder = MauiApp.CreateBuilder(); 19 | builder 20 | .UseMauiMicroMvvm( 21 | "Resources/Styles/Colors.xaml", 22 | "Resources/Styles/Styles.xaml") 23 | .UseMauiCommunityToolkit() 24 | .ConfigureFonts(fonts => 25 | { 26 | fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); 27 | fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); 28 | }); 29 | 30 | builder.Services.AddSingleton(WebAuthenticator.Default) 31 | .AddSingleton(AppleSignInAuthenticator.Default) 32 | .AddSingleton(SecureStorage.Default) 33 | .AddRefitClient() 34 | .AddTransient() 35 | .AddTransient(); 36 | 37 | return builder.Build(); 38 | } 39 | 40 | public static IServiceCollection AddRefitClient(this IServiceCollection services) 41 | where T : class 42 | { 43 | services.AddSingleton(sp => 44 | { 45 | var settings = new RefitSettings 46 | { 47 | AuthorizationHeaderValueGetter = () => sp.GetRequiredService().GetAsync("access_token") 48 | }; 49 | return RestService.For(Constants.BaseUrl, settings); 50 | }); 51 | return services; 52 | } 53 | } -------------------------------------------------------------------------------- /sample/DemoMobileApp/Platforms/Windows/Package.appxmanifest: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | $placeholder$ 12 | User Name 13 | $placeholder$.png 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | DemoMobileApp 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/ViewModels/MainPageViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using DemoMobileApp.Services; 3 | using MauiMicroMvvm; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace DemoMobileApp.ViewModels; 7 | 8 | public class MainPageViewModel : MauiMicroViewModel 9 | { 10 | private ISecureStorage _storage { get; } 11 | private IWebAuthenticator _webAuthenticator { get; } 12 | private IUserProfileService _userProfile { get; } 13 | 14 | public MainPageViewModel(ViewModelContext context, ISecureStorage storage, IWebAuthenticator webAuthenticator, IUserProfileService userProfile) 15 | : base(context) 16 | { 17 | _storage = storage; 18 | _webAuthenticator = webAuthenticator; 19 | _userProfile = userProfile; 20 | LoginCommand = new (OnLoginCommandExecuted); 21 | Claims = new(); 22 | } 23 | 24 | public ObservableCollection Claims { get; } 25 | 26 | public string Email 27 | { 28 | get => Get(); 29 | set => Set(value); 30 | } 31 | 32 | public Command LoginCommand { get; } 33 | 34 | private async void OnLoginCommandExecuted(string scheme) 35 | { 36 | try 37 | { 38 | var result = await _webAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions 39 | { 40 | CallbackUrl = new Uri($"{Constants.CallbackScheme}://"), 41 | Url = new Uri(new Uri(Constants.BaseUrl), $"mobileauth/{scheme}") 42 | }); 43 | 44 | await _storage.SetAsync("access_token", result.AccessToken); 45 | 46 | using var response = await _userProfile.GetProfileClaims(); 47 | if(response.IsSuccessStatusCode) 48 | { 49 | Claims.Clear(); 50 | var claims = response.Content.Select(x => $"{x.Key}: {x.Value}"); 51 | foreach (var claim in claims) 52 | Claims.Add(claim); 53 | 54 | if (response.Content.TryGetValue("email", out var email)) 55 | Email = email; 56 | } 57 | } 58 | catch (Exception ex) 59 | { 60 | Logger.LogError(ex, "Unexpected error occurred."); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sample/DemoAPI/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using AvantiPoint.MobileAuth; 3 | using DemoAPI.Data; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.OpenApi.Models; 6 | 7 | var builder = WebApplication.CreateBuilder(args); 8 | 9 | builder.AddMobileAuth(auth => 10 | { 11 | // Configure override for Token Store 12 | auth.ConfigureDbTokenStore(o => o.UseInMemoryDatabase("DemoApi")); 13 | // Configure override for Claims Handler 14 | auth.AddMobileAuthClaimsHandler(); 15 | 16 | // Add Additional Providers like Facebook, Twitter, LinkedIn, GitHub, etc... 17 | }); 18 | 19 | // Add services to the container. 20 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 21 | builder.Services.AddEndpointsApiExplorer(); 22 | var versionInfo = FileVersionInfo.GetVersionInfo(typeof(MobileAuth).Assembly.Location); 23 | var version = $"{versionInfo.FileMajorPart}.{versionInfo.ProductMinorPart}.{versionInfo.ProductBuildPart}"; 24 | builder.Services.AddSwaggerGen(c => 25 | { 26 | c.SwaggerDoc($"v1", new OpenApiInfo 27 | { 28 | Title = "Mobile Auth - Demo", 29 | Contact = new OpenApiContact 30 | { 31 | Name = "AvantiPoint", 32 | Email = "hello@avantipoint.com", 33 | Url = new Uri("https://avantipoint.com") 34 | }, 35 | Description = "This is a demo api for the AvantiPoint Mobile Auth library. Do not use this API for production. For more information please visit https://github.com/avantipoint/mobileauth-lib", 36 | Version = version 37 | }); 38 | }); 39 | 40 | var app = builder.Build(); 41 | 42 | // Configure the HTTP request pipeline. 43 | 44 | app.UseHttpsRedirection(); 45 | app.UseSwagger(); 46 | app.UseSwaggerUI(o => 47 | { 48 | o.InjectStylesheet("https://cdn.avantipoint.com/theme/swagger/style.css"); 49 | }); 50 | 51 | app.UseAuthentication(); 52 | app.UseAuthorization(); 53 | 54 | app.Map("/", async context => 55 | { 56 | await Task.CompletedTask; 57 | context.Response.Redirect("/swagger"); 58 | }); 59 | 60 | // maps https://{host}/mobileauth/{Apple|Google|Microsoft} 61 | app.MapDefaultMobileAuthRoutes(); 62 | //app.MapMobileAuthRoute(); 63 | //app.MapMobileAuthLogoutRoute(); 64 | //app.MapMobileAuthUserClaimsRoute("/profile"); 65 | 66 | app.Run(); 67 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | en 4 | True 5 | True 6 | Dan Siegel 7 | AvantiPoint 8 | Copyright © AvantiPoint 2016-$([System.DateTime]::Now.Year) 9 | LICENSE 10 | avantipoint-icon.png 11 | https://github.com/AvantiPoint/mobileauth-lib 12 | git 13 | https://github.com/AvantiPoint/mobileauth-lib.git 14 | $(MSBuildThisFileDirectory)Artifacts 15 | $(BUILD_ARTIFACTSTAGINGDIRECTORY) 16 | true 17 | false 18 | $(IsPackable) 19 | $(CI) 20 | 21 | 22 | 23 | 24 | snupkg 25 | true 26 | true 27 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 28 | 29 | 30 | 31 | 35 | 39 | 40 | 41 | 42 | 43 | 46 | 49 | 50 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/Resources/Styles/Colors.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | #512BD4 8 | #DFD8F7 9 | #2B0B98 10 | White 11 | Black 12 | #E1E1E1 13 | #C8C8C8 14 | #ACACAC 15 | #919191 16 | #6E6E6E 17 | #404040 18 | #212121 19 | #141414 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | #F7B548 35 | #FFD590 36 | #FFE5B9 37 | #28C2D1 38 | #7BDDEF 39 | #C3F2F4 40 | #3E8EED 41 | #72ACF1 42 | #A7CBF6 43 | 44 | -------------------------------------------------------------------------------- /src/AvantiPoint.MobileAuth/AvantiPoint.MobileAuth.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | enable 6 | enable 7 | true 8 | The MobileAuth Library is designed to quickly stand up an OAuth endpoint for Apple, Google, & Microsoft providers with the flexibility of customizing additional ones or only providing ones that you provide a configuration for. This makes it easy to stand up a minimal API with only a few lines of code. 9 | apple;google;microsoft;oauth;minimal api 10 | False 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /sample/DemoMobileApp/MainPage.xaml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 24 |