Added auth.
Unfortunately it does not refresh at first login. Have to figure out how to make it refresh
This commit is contained in:
parent
ebd109d850
commit
5fb95c8305
23 changed files with 406 additions and 32 deletions
|
@ -3,7 +3,7 @@
|
||||||
public App() {
|
public App() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
MainPage = new MainPage();
|
MainPage = new AppShell();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
AppShell.xaml
Normal file
14
AppShell.xaml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<Shell
|
||||||
|
x:Class="Neighbourhood.omg.lol.AppShell"
|
||||||
|
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
xmlns:local="clr-namespace:Neighbourhood.omg.lol"
|
||||||
|
Shell.FlyoutBehavior="Disabled"
|
||||||
|
Title="omg.lol Neighbourhood">
|
||||||
|
|
||||||
|
<ShellContent
|
||||||
|
ContentTemplate="{DataTemplate local:MainPage}"
|
||||||
|
Route="MainPage" />
|
||||||
|
|
||||||
|
</Shell>
|
11
AppShell.xaml.cs
Normal file
11
AppShell.xaml.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Neighbourhood.omg.lol;
|
||||||
|
|
||||||
|
public partial class AppShell : Shell
|
||||||
|
{
|
||||||
|
public AppShell()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Routing.RegisterRoute(nameof(LoginWebViewPage), typeof(LoginWebViewPage));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +1,39 @@
|
||||||
<nav class="left drawer l">
|
@inject CustomAuthenticationStateProvider AuthStateProvider;
|
||||||
|
<nav class="left drawer l">
|
||||||
<header>
|
<header>
|
||||||
<nav>
|
<nav>
|
||||||
<img src="https://cdn.cache.lol/img/prami.svg" class="circle">
|
<AuthorizeView>
|
||||||
<h6>Hey there</h6>
|
<Authorized>
|
||||||
|
<img class="circle medium" src="https://profiles.cache.lol/@FirstAddress/picture" alt="@FirstAddress" />
|
||||||
|
<div>Hey, @Name.</div>
|
||||||
|
|
||||||
|
</Authorized>
|
||||||
|
<NotAuthorized>
|
||||||
|
<img src="https://cdn.cache.lol/img/prami.svg" class="medium">
|
||||||
|
<div>
|
||||||
|
Hey there. <br/>
|
||||||
|
<a href="/login">Login?</a>
|
||||||
|
</div>
|
||||||
|
</NotAuthorized>
|
||||||
|
</AuthorizeView>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
<NavLink class="row nav-link" href="" Match="NavLinkMatch.All">
|
||||||
<i class="fa-solid fa-fw fa-home"></i>
|
<i class="fa-solid fa-fw fa-home"></i>
|
||||||
<div>Home</div>
|
<div>Home</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<NavLink class="nav-link" href="/statuslog/latest">
|
<NavLink class="row nav-link" href="/statuslog/latest">
|
||||||
<i><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="#4dabf7" d="M250 450c-38.388 0-76.775-14.646-106.066-43.934l-100-100c-58.579-58.58-58.579-153.553 0-212.132C100.534 37.336 191.105 35.421 250 88.191c58.898-52.768 149.47-50.853 206.066 5.743 58.58 58.58 58.58 153.553 0 212.132l-100 100C326.778 435.354 288.39 450 250 450" /><path fill="#228be6" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" stroke="#461036" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /><path fill="#1971c2" d="M438.68 212.011c0-32.564-26.399-58.962-58.963-58.962s-58.962 26.398-58.962 58.962 26.398 58.963 58.962 58.963 58.962-26.399 58.962-58.963m-259.433 0c0-32.564-26.398-58.962-58.962-58.962S61.32 179.447 61.32 212.011s26.398 58.963 58.963 58.963 58.962-26.399 58.962-58.963" /><path fill="#4dabf7" stroke="#1864ab" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /></svg></i>
|
<i><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="#4dabf7" d="M250 450c-38.388 0-76.775-14.646-106.066-43.934l-100-100c-58.579-58.58-58.579-153.553 0-212.132C100.534 37.336 191.105 35.421 250 88.191c58.898-52.768 149.47-50.853 206.066 5.743 58.58 58.58 58.58 153.553 0 212.132l-100 100C326.778 435.354 288.39 450 250 450" /><path fill="#228be6" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" stroke="#461036" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /><path fill="#1971c2" d="M438.68 212.011c0-32.564-26.399-58.962-58.963-58.962s-58.962 26.398-58.962 58.962 26.398 58.963 58.962 58.963 58.962-26.399 58.962-58.963m-259.433 0c0-32.564-26.398-58.962-58.962-58.962S61.32 179.447 61.32 212.011s26.398 58.963 58.963 58.963 58.962-26.399 58.962-58.963" /><path fill="#4dabf7" stroke="#1864ab" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /></svg></i>
|
||||||
<div>Statuslog</div>
|
<div>Statuslog</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
<div class="row max"></div>
|
||||||
|
<footer>
|
||||||
|
<AuthorizeView>
|
||||||
|
<Authorized><a class="button small" @onclick='() => AuthStateProvider.Logout()'>Logout</a></Authorized>
|
||||||
|
<NotAuthorized><a class="button small" href="/login">Login</a></NotAuthorized>
|
||||||
|
</AuthorizeView>
|
||||||
|
</footer>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<nav class="left m">
|
<nav class="left m">
|
||||||
|
@ -38,4 +59,19 @@
|
||||||
<i><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="#4dabf7" d="M250 450c-38.388 0-76.775-14.646-106.066-43.934l-100-100c-58.579-58.58-58.579-153.553 0-212.132C100.534 37.336 191.105 35.421 250 88.191c58.898-52.768 149.47-50.853 206.066 5.743 58.58 58.58 58.58 153.553 0 212.132l-100 100C326.778 435.354 288.39 450 250 450" /><path fill="#228be6" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" stroke="#461036" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /><path fill="#1971c2" d="M438.68 212.011c0-32.564-26.399-58.962-58.963-58.962s-58.962 26.398-58.962 58.962 26.398 58.963 58.962 58.963 58.962-26.399 58.962-58.963m-259.433 0c0-32.564-26.398-58.962-58.962-58.962S61.32 179.447 61.32 212.011s26.398 58.963 58.963 58.963 58.962-26.399 58.962-58.963" /><path fill="#4dabf7" stroke="#1864ab" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /></svg></i>
|
<i><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><path fill="#4dabf7" d="M250 450c-38.388 0-76.775-14.646-106.066-43.934l-100-100c-58.579-58.58-58.579-153.553 0-212.132C100.534 37.336 191.105 35.421 250 88.191c58.898-52.768 149.47-50.853 206.066 5.743 58.58 58.58 58.58 153.553 0 212.132l-100 100C326.778 435.354 288.39 450 250 450" /><path fill="#228be6" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M220.52 176.634a11.792 11.792 0 1 1-23.586 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0" /><path fill="#1864ab" stroke="#1864ab" stroke-miterlimit="10" stroke-width="11.32074" d="M303.066 176.634a11.792 11.792 0 1 1-23.585 0 11.792 11.792 0 0 1 23.585 0Z" /><path fill="#228be6" stroke="#461036" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /><path fill="#1971c2" d="M438.68 212.011c0-32.564-26.399-58.962-58.963-58.962s-58.962 26.398-58.962 58.962 26.398 58.963 58.962 58.963 58.962-26.399 58.962-58.963m-259.433 0c0-32.564-26.398-58.962-58.962-58.962S61.32 179.447 61.32 212.011s26.398 58.963 58.963 58.963 58.962-26.399 58.962-58.963" /><path fill="#4dabf7" stroke="#1864ab" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="18.8679" d="M208.75 223.817c18.875 30.802 63.626 30.802 82.501 0" /></svg></i>
|
||||||
<div>Statuslog</div>
|
<div>Statuslog</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private string? Name = null;
|
||||||
|
private List<string> Addresses = new List<string>();
|
||||||
|
private string FirstAddress { get => this.Addresses.FirstOrDefault() ?? string.Empty; }
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync() {
|
||||||
|
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var identity = state.User.Identity;
|
||||||
|
|
||||||
|
Name = identity?.Name ?? string.Empty;
|
||||||
|
Addresses = state.User.FindFirst("addresses")?.Value?.Split(',')?.ToList() ?? new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
@page "/"
|
@page "/"
|
||||||
|
@inject CustomAuthenticationStateProvider AuthStateProvider;
|
||||||
|
|
||||||
<h1>Hello, lol!</h1>
|
<h1><i data-emoji="👋"></i> Hello, lol!</h1>
|
||||||
|
<a @onclick='() => AuthStateProvider.Logout()'>Logout</a>
|
||||||
|
|
||||||
Welcome to your new app.
|
Welcome to your new app.
|
29
Components/Pages/Login.razor
Normal file
29
Components/Pages/Login.razor
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@page "/login"
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@using System.Security.Claims
|
||||||
|
@inject NavigationManager navigationManager
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
<h3>Login</h3>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync() {
|
||||||
|
await checkLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// protected override async Task OnAfterRenderAsync(bool firstRender) {
|
||||||
|
// await checkLogin();
|
||||||
|
// }
|
||||||
|
|
||||||
|
private async Task checkLogin() {
|
||||||
|
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
var user = authState.User;
|
||||||
|
|
||||||
|
if (user.Identity is not null && user.Identity.IsAuthenticated) {
|
||||||
|
navigationManager.NavigateTo("/");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await Shell.Current.GoToAsync(nameof(LoginWebViewPage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Components/RedirectToLogin.razor
Normal file
8
Components/RedirectToLogin.razor
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
@inject NavigationManager navigationManager
|
||||||
|
<div class="loader loader-bouncing"><span>Redirecting...</span></div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
protected override void OnInitialized() {
|
||||||
|
navigationManager.NavigateTo("/login");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,18 @@
|
||||||
<Router AppAssembly="@typeof(MauiProgram).Assembly">
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
<Found Context="routeData">
|
<Router AppAssembly="@typeof(MauiProgram).Assembly">
|
||||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
|
<Found Context="routeData">
|
||||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)">
|
||||||
</Found>
|
<Authorizing>Logging in...</Authorizing>
|
||||||
|
<NotAuthorized><RedirectToLogin /></NotAuthorized>
|
||||||
|
</AuthorizeRouteView>
|
||||||
|
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||||
|
</Found>
|
||||||
|
<NotFound>
|
||||||
|
<CascadingAuthenticationState>
|
||||||
|
<LayoutView Layout="@typeof(Layout.MainLayout)">
|
||||||
|
<img data-emoji="🦒" />
|
||||||
|
<p>Sorry, there's nothing here.</p>
|
||||||
|
</LayoutView>
|
||||||
|
</CascadingAuthenticationState>
|
||||||
|
</NotFound>
|
||||||
</Router>
|
</Router>
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||||
|
@using Microsoft.AspNetCore.Components.Authorization
|
||||||
|
@using Microsoft.AspNetCore.Authorization
|
||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using Neighbourhood.omg.lol
|
@using Neighbourhood.omg.lol
|
||||||
@using Neighbourhood.omg.lol.Components
|
@using Neighbourhood.omg.lol.Components
|
||||||
|
|
70
CustomAuthenticationStateProvider.cs
Normal file
70
CustomAuthenticationStateProvider.cs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Neighbourhood.omg.lol.Models;
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol {
|
||||||
|
public class CustomAuthenticationStateProvider : AuthenticationStateProvider {
|
||||||
|
public CustomAuthenticationStateProvider() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Login(string token) {
|
||||||
|
await SecureStorage.SetAsync("accounttoken", token);
|
||||||
|
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Logout() {
|
||||||
|
SecureStorage.Remove("accounttoken");
|
||||||
|
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<AuthenticationState> GetAuthenticationStateAsync() {
|
||||||
|
var identity = new ClaimsIdentity();
|
||||||
|
try {
|
||||||
|
var token = await SecureStorage.GetAsync("accounttoken");
|
||||||
|
if (token != null) {
|
||||||
|
var name = await SecureStorage.GetAsync("accountname");
|
||||||
|
var email = await SecureStorage.GetAsync("accountemail");
|
||||||
|
var addresses = await SecureStorage.GetAsync("accountaddresses");
|
||||||
|
|
||||||
|
RestService api = new RestService(token);
|
||||||
|
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(email)) {
|
||||||
|
AccountResponseData? accountInfo = await api.AccountInfo();
|
||||||
|
if (accountInfo != null) {
|
||||||
|
name = accountInfo.Name;
|
||||||
|
email = accountInfo.Email;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(email)) {
|
||||||
|
if (string.IsNullOrEmpty(addresses)) {
|
||||||
|
AddressResponseList? addressList = await api.Addresses();
|
||||||
|
List<string> addressStrings = new List<string>();
|
||||||
|
if (addressList != null) foreach (var address in addressList) {
|
||||||
|
if (!address.Expiration.Expired && !string.IsNullOrEmpty(address.Address)) {
|
||||||
|
addressStrings.Add(address.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addresses = string.Join(',', addressStrings);
|
||||||
|
}
|
||||||
|
if(!string.IsNullOrEmpty(addresses)) {
|
||||||
|
await SecureStorage.SetAsync("accountname", name);
|
||||||
|
await SecureStorage.SetAsync("accountemail", email);
|
||||||
|
await SecureStorage.SetAsync("accountaddresses", addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
var claims = new[] {
|
||||||
|
new Claim(ClaimTypes.Name, name),
|
||||||
|
new Claim(ClaimTypes.Email, email),
|
||||||
|
new Claim("addresses", addresses)
|
||||||
|
};
|
||||||
|
identity = new ClaimsIdentity(claims, "Server authentication");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex) {
|
||||||
|
Console.WriteLine("Request failed:" + ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AuthenticationState(new ClaimsPrincipal(identity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
LoginWebViewPage.xaml
Normal file
12
LoginWebViewPage.xaml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="Neighbourhood.omg.lol.LoginWebViewPage"
|
||||||
|
BackgroundColor="{DynamicResource PageBackgroundColor}">
|
||||||
|
<Grid>
|
||||||
|
<WebView x:Name="loginwebview"
|
||||||
|
Navigating="loginwebview_Navigating"
|
||||||
|
VerticalOptions="Fill"
|
||||||
|
HorizontalOptions="Fill" />
|
||||||
|
</Grid>
|
||||||
|
</ContentPage>
|
37
LoginWebViewPage.xaml.cs
Normal file
37
LoginWebViewPage.xaml.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Web;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol;
|
||||||
|
|
||||||
|
public partial class LoginWebViewPage : ContentPage
|
||||||
|
{
|
||||||
|
private AuthenticationStateProvider AuthStateProvider { get; set; }
|
||||||
|
|
||||||
|
public LoginWebViewPage(AuthenticationStateProvider authStateProvider)
|
||||||
|
{
|
||||||
|
this.AuthStateProvider = authStateProvider;
|
||||||
|
InitializeComponent();
|
||||||
|
this.loginwebview.Source = "https://home.omg.lol/oauth/authorize?client_id=ea14dafd3e92cbcf93750c35cd81a031&scope=everything&redirect_uri=https://neatnik.net/adam/bucket/omgloloauth/&response_type=code";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void loginwebview_Navigating(object sender, WebNavigatingEventArgs e) {
|
||||||
|
if(e.Url.StartsWith("https://neatnik.net/adam/bucket/omgloloauth/")) {
|
||||||
|
Debug.WriteLine("And here we go...");
|
||||||
|
Uri uri = new Uri(e.Url);
|
||||||
|
var query = HttpUtility.ParseQueryString(uri.Query);
|
||||||
|
string? code = query.Get("code");
|
||||||
|
if (!string.IsNullOrEmpty(code)) {
|
||||||
|
RestService api = new RestService();
|
||||||
|
string? token = await api.OAuth(code);
|
||||||
|
if (!string.IsNullOrEmpty(token)) {
|
||||||
|
Debug.WriteLine($"Fuck yeah, a token! {token}");
|
||||||
|
await ((CustomAuthenticationStateProvider)this.AuthStateProvider).Login(token);
|
||||||
|
await Shell.Current.GoToAsync("..");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
x:Class="Neighbourhood.omg.lol.MainPage"
|
x:Class="Neighbourhood.omg.lol.MainPage"
|
||||||
BackgroundColor="{DynamicResource PageBackgroundColor}">
|
BackgroundColor="{DynamicResource PageBackgroundColor}">
|
||||||
|
|
||||||
<BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html">
|
<BlazorWebView x:Name="blazorWebView" HostPage="wwwroot/index.html" UrlLoading="BlazorUrlLoading">
|
||||||
<BlazorWebView.RootComponents>
|
<BlazorWebView.RootComponents>
|
||||||
<RootComponent Selector="#app" ComponentType="{x:Type local:Components.Routes}" />
|
<RootComponent Selector="#app" ComponentType="{x:Type local:Components.Routes}" />
|
||||||
</BlazorWebView.RootComponents>
|
</BlazorWebView.RootComponents>
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
namespace Neighbourhood.omg.lol {
|
using Microsoft.AspNetCore.Components.WebView;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol {
|
||||||
public partial class MainPage : ContentPage {
|
public partial class MainPage : ContentPage {
|
||||||
public MainPage() {
|
public MainPage() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BlazorUrlLoading(object? sender, UrlLoadingEventArgs e) {
|
||||||
|
if(e.Url.Host == "home.omg.lol" && e.Url.AbsolutePath == "/oauth/authorize") {
|
||||||
|
e.UrlLoadingStrategy = UrlLoadingStrategy.CancelLoad;
|
||||||
|
Shell.Current.GoToAsync(nameof(LoginWebViewPage));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.AspNetCore.Components.Authorization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Neighbourhood.omg.lol {
|
namespace Neighbourhood.omg.lol {
|
||||||
public static class MauiProgram {
|
public static class MauiProgram {
|
||||||
|
@ -11,9 +12,14 @@ namespace Neighbourhood.omg.lol {
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddMauiBlazorWebView();
|
builder.Services.AddMauiBlazorWebView();
|
||||||
|
builder.Services.AddTransient<LoginWebViewPage>();
|
||||||
|
|
||||||
|
builder.Services.AddAuthorizationCore();
|
||||||
|
builder.Services.AddScoped<CustomAuthenticationStateProvider>();
|
||||||
|
builder.Services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<CustomAuthenticationStateProvider>());
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
builder.Services.AddBlazorWebViewDeveloperTools();
|
builder.Services.AddBlazorWebViewDeveloperTools();
|
||||||
builder.Logging.AddDebug();
|
builder.Logging.AddDebug();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
14
Models/AccountResponseData.cs
Normal file
14
Models/AccountResponseData.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
|
public class AccountResponseData : IOmgLolResponseData {
|
||||||
|
public string Message { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
// created, api_key and settings
|
||||||
|
}
|
||||||
|
}
|
41
Models/AddressResponseData.cs
Normal file
41
Models/AddressResponseData.cs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
|
public class AddressResponseData : IOmgLolResponseData {
|
||||||
|
public string Address { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public RegistrationData Registration { get; set; }
|
||||||
|
public ExpirationData Expiration { get; set; }
|
||||||
|
|
||||||
|
public class TimeData {
|
||||||
|
public long? UnixEpochTime { get; set; }
|
||||||
|
public string? Iso8601Time { get; set; }
|
||||||
|
public string? Rfc2822Time { get; set; }
|
||||||
|
public string? RelativeTime { get; set; }
|
||||||
|
public PreferenceData? Preferences { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RegistrationData : TimeData {
|
||||||
|
public string? Message { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExpirationData : TimeData {
|
||||||
|
public bool Expired { get; set; }
|
||||||
|
public bool WillExpire { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PreferenceData {
|
||||||
|
public string? IncludeInDirectory { get; set; }
|
||||||
|
public string? ShowOnDashboard { get; set; }
|
||||||
|
public StatuslogPreferenceData? Statuslog { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StatuslogPreferenceData {
|
||||||
|
public bool? MastodonPosting { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
Models/AddressResponseList.cs
Normal file
10
Models/AddressResponseList.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
|
public class AddressResponseList : List<AddressResponseData>, IOmgLolResponseList<AddressResponseData> {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
namespace Neighbourhood.omg.lol.Models {
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
public interface IOmgLolResponseData {
|
public interface IOmgLolResponseData {
|
||||||
string Message { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
Models/IOmgLolResponseList.cs
Normal file
10
Models/IOmgLolResponseList.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
|
public interface IOmgLolResponseList<T> : IList<T>, IOmgLolResponseData where T : IOmgLolResponseData {
|
||||||
|
}
|
||||||
|
}
|
7
Models/TokenResponseData.cs
Normal file
7
Models/TokenResponseData.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Neighbourhood.omg.lol.Models {
|
||||||
|
public class TokenResponseData {
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
public string TokenType { get; set; }
|
||||||
|
public string Scope { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,10 +60,23 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||||
<PackageReference Include="Markdig" Version="0.37.0" />
|
<PackageReference Include="Markdig" Version="0.37.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
|
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
|
||||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
|
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="$(MauiVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="$(MauiVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.40" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<MauiXaml Update="AppShell.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</MauiXaml>
|
||||||
|
<MauiXaml Update="LoginWebViewPage.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</MauiXaml>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
using Markdig;
|
using Markdig;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Neighbourhood.omg.lol.Models;
|
using Neighbourhood.omg.lol.Models;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Neighbourhood.omg.lol {
|
namespace Neighbourhood.omg.lol {
|
||||||
public class RestService {
|
public class RestService {
|
||||||
HttpClient _client;
|
HttpClient _client;
|
||||||
JsonSerializerOptions _serializerOptions;
|
JsonSerializerOptions _serializerOptions;
|
||||||
|
public const string BaseUrl = "https://api.omg.lol";
|
||||||
|
|
||||||
public RestService() {
|
public RestService(string? token = null) {
|
||||||
_client = new HttpClient();
|
_client = new HttpClient();
|
||||||
_serializerOptions = new JsonSerializerOptions {
|
_serializerOptions = new JsonSerializerOptions {
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||||
WriteIndented = true
|
WriteIndented = true
|
||||||
};
|
};
|
||||||
|
addToken(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<T?> GetResponse<T>(Uri uri) where T:IOmgLolResponseData {
|
private void addToken(string? token = null) {
|
||||||
|
if (token == null) token = Task.Run(() => SecureStorage.GetAsync("accounttoken")).GetAwaiter().GetResult();
|
||||||
|
if (token != null) _client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<T?> Get<T>(Uri uri) where T:IOmgLolResponseData {
|
||||||
T? responseData = default(T);
|
T? responseData = default(T);
|
||||||
try {
|
try {
|
||||||
HttpResponseMessage response = await _client.GetAsync(uri);
|
HttpResponseMessage response = await _client.GetAsync(uri);
|
||||||
|
@ -42,20 +44,49 @@ namespace Neighbourhood.omg.lol {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Status>> StatuslogLatest() {
|
public async Task<List<Status>> StatuslogLatest() {
|
||||||
Uri uri = new Uri("https://api.omg.lol/statuslog/latest");
|
Uri uri = new Uri($"{BaseUrl}/statuslog/latest");
|
||||||
return (await GetResponse<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
return (await Get<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Status>> Statuslog(string address) {
|
public async Task<List<Status>> Statuslog(string address) {
|
||||||
Uri uri = new Uri($"https://api.omg.lol/address/{address}/statuses");
|
Uri uri = new Uri($"{BaseUrl}/address/{address}/statuses");
|
||||||
return (await GetResponse<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
return (await Get<StatusResponseData>(uri))?.Statuses ?? new List<Status>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<MarkupString> StatuslogBio(string address) {
|
public async Task<MarkupString> StatuslogBio(string address) {
|
||||||
Uri uri = new Uri($"https://api.omg.lol/address/{address}/statuses/bio");
|
Uri uri = new Uri($"{BaseUrl}/address/{address}/statuses/bio");
|
||||||
StatusBioResponseData? responseData = await GetResponse<StatusBioResponseData>(uri);
|
StatusBioResponseData? responseData = await Get<StatusBioResponseData>(uri);
|
||||||
Debug.WriteLine(responseData?.Bio);
|
|
||||||
return (MarkupString)Markdown.ToHtml(responseData?.Bio ?? "");
|
return (MarkupString)Markdown.ToHtml(responseData?.Bio ?? "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<AccountResponseData?> AccountInfo() {
|
||||||
|
Uri uri = new Uri($"{BaseUrl}/account/application/info");
|
||||||
|
AccountResponseData? responseData = await Get<AccountResponseData>(uri);
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AddressResponseList?> Addresses() {
|
||||||
|
Uri uri = new Uri($"{BaseUrl}/account/application/addresses");
|
||||||
|
AddressResponseList? responseData = await Get<AddressResponseList>(uri);
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string?> OAuth(string code) {
|
||||||
|
string? token = null;
|
||||||
|
Uri uri = new Uri($"{BaseUrl}/oauth/?code={code}&client_id=ea14dafd3e92cbcf93750c35cd81a031&client_secret=ec28b8653f1d98b4eef3f7a20858c43b&redirect_uri=https://neatnik.net/adam/bucket/omgloloauth/&scope=everything");
|
||||||
|
try {
|
||||||
|
HttpResponseMessage response = await _client.GetAsync(uri);
|
||||||
|
if (response.IsSuccessStatusCode) {
|
||||||
|
TokenResponseData? responseObj = await response.Content.ReadFromJsonAsync<TokenResponseData>(_serializerOptions);
|
||||||
|
if (responseObj != null && !string.IsNullOrEmpty(responseObj.AccessToken)) {
|
||||||
|
token = responseObj.AccessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Debug.WriteLine(@"\tERROR {0}", ex.Message);
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue