72 lines
2.4 KiB
Forth
72 lines
2.4 KiB
Forth
module JobsJobsJobs.Api.Auth
|
|
|
|
open FSharp.Json
|
|
open JWT
|
|
|
|
/// A JWT (de)serializer utilizing FSharp.Json
|
|
type FSharpJsonSerializer () =
|
|
interface IJsonSerializer with
|
|
member __.Serialize (any : obj) =
|
|
Json.serialize any
|
|
member __.Deserialize<'T> json =
|
|
Json.deserialize<'T> json
|
|
|
|
|
|
open Data
|
|
open Domain
|
|
open JWT.Algorithms
|
|
open JWT.Builder
|
|
open System
|
|
open System.Net.Http
|
|
open System.Net.Http.Headers
|
|
open JWT.Exceptions
|
|
|
|
/// Verify a user's credentials with No Agenda Social
|
|
let verifyWithMastodon accessToken = async {
|
|
use client = new HttpClient ()
|
|
use req = new HttpRequestMessage (HttpMethod.Get, $"{config.auth.apiUrl}accounts/verify_credentials")
|
|
req.Headers.Authorization <- AuthenticationHeaderValue $"Bearer {accessToken}"
|
|
match! client.SendAsync req |> Async.AwaitTask with
|
|
| res when res.IsSuccessStatusCode ->
|
|
let! body = res.Content.ReadAsStringAsync ()
|
|
return
|
|
match Json.deserialize<ViewModels.Citizen.MastodonAccount> body with
|
|
| profile when profile.username = profile.acct -> Ok profile
|
|
| profile -> Error $"Profiles must be from noagendasocial.com; yours is {profile.acct}"
|
|
| res -> return Error $"Could not retrieve credentials: %d{int res.StatusCode} ~ {res.ReasonPhrase}"
|
|
}
|
|
|
|
/// Create a JWT for the given user
|
|
let createJwt citizenId = async {
|
|
match! Citizens.tryFind citizenId with
|
|
| Ok (Some citizen) ->
|
|
return
|
|
JwtBuilder()
|
|
.WithAlgorithm(HMACSHA256Algorithm ())
|
|
// TODO: generate separate secret for server
|
|
.WithSecret(config.auth.secret)
|
|
.WithSerializer(FSharpJsonSerializer ())
|
|
.AddClaim("sub", CitizenId.toString citizen.id)
|
|
.AddClaim("exp", DateTimeOffset.UtcNow.AddHours(1.).ToUnixTimeSeconds ())
|
|
.AddClaim("nam", citizen.displayName)
|
|
.Encode ()
|
|
|> Ok
|
|
| Ok None -> return Error (exn "Citizen record not found")
|
|
| Error exn -> return Error exn
|
|
}
|
|
|
|
/// Validate the given token
|
|
let validateJwt token =
|
|
try
|
|
let paylod =
|
|
JwtBuilder()
|
|
.WithAlgorithm(HMACSHA256Algorithm ())
|
|
// TODO: generate separate secret for server
|
|
.WithSecret(config.auth.secret)
|
|
.MustVerifySignature()
|
|
.Decode<Map<string, obj>> token
|
|
CitizenId.tryParse (paylod.["sub"] :?> string)
|
|
with
|
|
| :? TokenExpiredException -> Error "Token is expired"
|
|
| :? SignatureVerificationException -> Error "Invalid token signature"
|