parent
2b7dd09630
commit
db40a5cffd
55
src/JobsJobsJobs.Api/Auth.fs
Normal file
55
src/JobsJobsJobs.Api/Auth.fs
Normal file
@ -0,0 +1,55 @@
|
||||
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 System.Net.Http
|
||||
open System.Net.Http.Headers
|
||||
open Data
|
||||
open JWT.Builder
|
||||
open JobsJobsJobs.Api.Domain
|
||||
open JWT.Algorithms
|
||||
open System
|
||||
|
||||
/// Verify a user's credentials with No Agenda Social
|
||||
let verifyWithMastodon accessToken = async {
|
||||
use client = new HttpClient ()
|
||||
use req = new HttpRequestMessage (HttpMethod.Get, (sprintf "%saccounts/verify_credentials" config.auth.apiUrl))
|
||||
req.Headers.Authorization <- AuthenticationHeaderValue <| sprintf "Bearer %s" 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 (sprintf "Profiles must be from noagendasocial.com; yours is %s" profile.acct)
|
||||
| res -> return Error (sprintf "Could not retrieve credentials: %d ~ %s" (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
|
||||
}
|
@ -18,26 +18,6 @@ module private Internal =
|
||||
>=> setMimeType "application/json; charset=utf-8"
|
||||
|
||||
|
||||
module Auth =
|
||||
|
||||
open System.Net.Http
|
||||
open System.Net.Http.Headers
|
||||
|
||||
/// Verify a user's credentials with No Agenda Social
|
||||
let verifyWithMastodon accessToken = async {
|
||||
use client = new HttpClient ()
|
||||
use req = new HttpRequestMessage (HttpMethod.Get, (sprintf "%saccounts/verify_credentials" config.auth.apiUrl))
|
||||
req.Headers.Authorization <- AuthenticationHeaderValue <| sprintf "Bearer %s" 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 (sprintf "Profiles must be from noagendasocial.com; yours is %s" profile.acct)
|
||||
| res -> return Error (sprintf "Could not retrieve credentials: %d ~ %s" (int res.StatusCode) res.ReasonPhrase)
|
||||
}
|
||||
|
||||
/// Handler to return the Vue application
|
||||
module Vue =
|
||||
|
||||
@ -113,11 +93,9 @@ module Citizen =
|
||||
| Ok idResult ->
|
||||
match! establishCitizen idResult profile with
|
||||
| Ok citizenId ->
|
||||
// TODO: replace this with a JWT issued by the server user
|
||||
match! Citizens.tryFind citizenId with
|
||||
| Ok (Some citizen) -> return! json citizen ctx
|
||||
| Ok None -> return! Error.error (exn ()) "Citizen record not found" ctx
|
||||
| Error exn -> return! Error.error exn "Could not retrieve user from database" ctx
|
||||
match! Auth.createJwt citizenId with
|
||||
| Ok jwt -> return! json {| accessToken = jwt |} ctx
|
||||
| Error exn -> return! Error.error exn "Could not issue access token" ctx
|
||||
| Error exn -> return! Error.error exn "Could not update Jobs, Jobs, Jobs database" ctx
|
||||
| Error exn -> return! Error.error exn "Token not received" ctx
|
||||
| Error msg ->
|
||||
|
@ -10,12 +10,14 @@
|
||||
<Compile Include="Extensions.fs" />
|
||||
<Compile Include="Data.fs" />
|
||||
<Compile Include="ViewModels.fs" />
|
||||
<Compile Include="Auth.fs" />
|
||||
<Compile Include="Handlers.fs" />
|
||||
<Compile Include="Program.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FSharp.Json" Version="0.4.0" />
|
||||
<PackageReference Include="JWT" Version="7.2.1" />
|
||||
<PackageReference Include="Markdig" Version="0.21.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.8" />
|
||||
|
499
src/jobs-jobs-jobs/package-lock.json
generated
499
src/jobs-jobs-jobs/package-lock.json
generated
@ -1807,6 +1807,180 @@
|
||||
"tslint": "^5.20.1",
|
||||
"webpack": "^4.0.0",
|
||||
"yorkie": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.1.0",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.7.2"
|
||||
}
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"fork-ts-checker-webpack-plugin-v5": {
|
||||
"version": "npm:fork-ts-checker-webpack-plugin@5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.1.0.tgz",
|
||||
"integrity": "sha512-vuKyEjSLGbhQbEr5bifXXOkr9iV73L6n72mHoHIv7okvrf7O7z6RKeplM6C6ATPsukoQivij+Ba1vcptL60Z2g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.8.3",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"chalk": "^4.1.0",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"fs-extra": "^9.0.0",
|
||||
"memfs": "^3.1.2",
|
||||
"minimatch": "^3.0.4",
|
||||
"schema-utils": "2.7.0",
|
||||
"semver": "^7.3.2",
|
||||
"tapable": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
|
||||
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
|
||||
"integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
|
||||
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
|
||||
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@vue/cli-plugin-vuex": {
|
||||
@ -1885,6 +2059,17 @@
|
||||
"integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"cacache": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
|
||||
@ -1911,6 +2096,34 @@
|
||||
"unique-filename": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"find-cache-dir": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
|
||||
@ -1932,6 +2145,13 @@
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
@ -1996,6 +2216,16 @@
|
||||
"minipass": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
|
||||
@ -2012,6 +2242,21 @@
|
||||
"terser": "^4.6.12",
|
||||
"webpack-sources": "^1.4.3"
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.0.0-beta.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.7.tgz",
|
||||
"integrity": "sha512-xQ8/GZmRPdQ3EinnE0IXwdVoDzh7Dowo0MowoyBuScEBXrRabw6At5/IdtD3waKklKW5PGokPsm8KRN6rvQ1cw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/mini-css-extract-plugin": "^0.9.1",
|
||||
"chalk": "^3.0.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"loader-utils": "^1.2.3",
|
||||
"merge-source-map": "^1.1.0",
|
||||
"source-map": "^0.6.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -5820,180 +6065,6 @@
|
||||
"worker-rpc": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fork-ts-checker-webpack-plugin-v5": {
|
||||
"version": "npm:fork-ts-checker-webpack-plugin@5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-5.1.0.tgz",
|
||||
"integrity": "sha512-vuKyEjSLGbhQbEr5bifXXOkr9iV73L6n72mHoHIv7okvrf7O7z6RKeplM6C6ATPsukoQivij+Ba1vcptL60Z2g==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.8.3",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"chalk": "^4.1.0",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"fs-extra": "^9.0.0",
|
||||
"memfs": "^3.1.2",
|
||||
"minimatch": "^3.0.4",
|
||||
"schema-utils": "2.7.0",
|
||||
"semver": "^7.3.2",
|
||||
"tapable": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.1.0",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.7.2"
|
||||
}
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
|
||||
"integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
|
||||
"integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz",
|
||||
"integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6",
|
||||
"universalify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz",
|
||||
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.4",
|
||||
"ajv": "^6.12.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
|
||||
"integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz",
|
||||
"integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
@ -11595,86 +11666,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-loader-v16": {
|
||||
"version": "npm:vue-loader@16.0.0-beta.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.5.tgz",
|
||||
"integrity": "sha512-ciWfzNefqWlmzKznCWY9hl+fPP4KlQ0A9MtHbJ/8DpyY+dAM8gDrjufIdxwTgC4szE4EZC3A6ip/BbrqM84GqA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/mini-css-extract-plugin": "^0.9.1",
|
||||
"chalk": "^3.0.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"loader-utils": "^1.2.3",
|
||||
"merge-source-map": "^1.1.0",
|
||||
"source-map": "^0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
|
||||
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"@types/color-name": "^1.1.1",
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
|
||||
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "4.0.0-beta.9",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.0-beta.9.tgz",
|
||||
|
77
src/jobs-jobs-jobs/src/api/index.ts
Normal file
77
src/jobs-jobs-jobs/src/api/index.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Jobs, Jobs, Jobs API interface
|
||||
*/
|
||||
|
||||
/** The base URL for the Jobs, Jobs, Jobs API */
|
||||
const API_URL = `${location.protocol}//${location.host}/api`
|
||||
|
||||
/** Local storage key for the Jobs, Jobs, Jobs access token */
|
||||
const JJJ_TOKEN = 'jjj-token'
|
||||
|
||||
/**
|
||||
* A holder for the JSON Web Token (JWT) returned from Jobs, Jobs, Jobs
|
||||
*/
|
||||
class JwtHolder {
|
||||
private jwt: string | null = null
|
||||
|
||||
/**
|
||||
* Get the current token (refreshing from local storage if needed).
|
||||
*/
|
||||
get token(): string | null {
|
||||
if (!this.jwt) this.jwt = localStorage.getItem(JJJ_TOKEN)
|
||||
return this.jwt
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current token (both here and in local storage).
|
||||
*
|
||||
* @param tokn The token to be set
|
||||
*/
|
||||
set token(tokn: string | null) {
|
||||
if (tokn) localStorage.setItem(JJJ_TOKEN, tokn); else localStorage.removeItem(JJJ_TOKEN)
|
||||
this.jwt = tokn
|
||||
}
|
||||
|
||||
get hasToken(): boolean {
|
||||
return this.token !== null
|
||||
}
|
||||
}
|
||||
|
||||
/** The user's current JWT */
|
||||
const jwt = new JwtHolder()
|
||||
|
||||
/**
|
||||
* Execute an HTTP request using the fetch API.
|
||||
*
|
||||
* @param url The URL to which the request should be made
|
||||
* @param method The HTTP method for the request (defaults to GET)
|
||||
* @param payload The payload to send along with the request (defaults to none)
|
||||
* @returns The response (if the request is successful)
|
||||
* @throws An error (if the request is unsuccessful)
|
||||
*/
|
||||
export async function doRequest(url: string, method?: string, payload?: string) {
|
||||
const headers: [string, string][] = [ [ 'Content-Type', 'application/json' ] ]
|
||||
if (jwt.hasToken) headers.push([ 'Authorization', `Bearer ${jwt.token}`])
|
||||
const options: RequestInit = {
|
||||
method: method || 'GET',
|
||||
headers: headers
|
||||
}
|
||||
if (method === 'POST' && payload) options.body = payload
|
||||
const actualUrl = (options.method === 'GET' && payload) ? `url?${payload}` : url
|
||||
const resp = await fetch(actualUrl, options)
|
||||
if (resp.ok) return resp
|
||||
throw new Error(`Error executing API request: ${resp.status} ~ ${resp.statusText}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize with Jobs, Jobs, Jobs using a No Agenda Social token.
|
||||
*
|
||||
* @param nasToken The token obtained from No Agenda Social
|
||||
* @returns True if it is successful
|
||||
*/
|
||||
export async function jjjAuthorize(nasToken: string): Promise<boolean> {
|
||||
const resp = await doRequest(`${API_URL}/citizen/log-on`, 'POST', JSON.stringify({ accessToken: nasToken }))
|
||||
const jjjToken = await resp.json()
|
||||
jwt.token = jjjToken.accessToken
|
||||
return true
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
* @version 1
|
||||
*/
|
||||
import { CLIENT_SECRET } from './config'
|
||||
import { doRequest, jjjAuthorize } from '../api'
|
||||
|
||||
/** Client ID for Jobs, Jobs, Jobs */
|
||||
const CLIENT_ID = '6Ook3LBff00dOhyBgbf4eXSqIpAroK72aioIdGaDqxs'
|
||||
@ -14,9 +15,6 @@ const CLIENT_ID = '6Ook3LBff00dOhyBgbf4eXSqIpAroK72aioIdGaDqxs'
|
||||
/** No Agenda Social's base URL */
|
||||
const NAS_URL = 'https://noagendasocial.com/'
|
||||
|
||||
/** No Agenda Social's base API URL */
|
||||
const API_URL = `${NAS_URL}api/v1/`
|
||||
|
||||
/** The base URL for Jobs, Jobs, Jobs */
|
||||
const JJJ_URL = `${location.protocol}//${location.host}/`
|
||||
|
||||
@ -37,27 +35,29 @@ export function authorize() {
|
||||
location.assign(`${NAS_URL}oauth/authorize?${params}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Log on a user with an authorzation code.
|
||||
*
|
||||
* @param authCode The authorization code obtained from No Agenda Social
|
||||
*/
|
||||
export async function logOn(authCode: string) {
|
||||
const options: RequestInit = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
client_id: CLIENT_ID,
|
||||
client_secret: CLIENT_SECRET,
|
||||
redirect_uri: `${JJJ_URL}user/authorized`,
|
||||
grant_type: 'authorization_code',
|
||||
code: authCode,
|
||||
scope: 'read'
|
||||
}),
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
const resp = await fetch(`${NAS_URL}oauth/token`, options)
|
||||
if (resp.ok) {
|
||||
try {
|
||||
const resp = await doRequest(`${NAS_URL}oauth/token`, 'POST',
|
||||
JSON.stringify({
|
||||
client_id: CLIENT_ID,
|
||||
client_secret: CLIENT_SECRET,
|
||||
redirect_uri: `${JJJ_URL}user/authorized`,
|
||||
grant_type: 'authorization_code',
|
||||
code: authCode,
|
||||
scope: 'read'
|
||||
})
|
||||
)
|
||||
const token = await resp.json()
|
||||
// TODO: submit token to server, let server obtain profile from NA Social
|
||||
await jjjAuthorize(token.access_code)
|
||||
// TODO: navigate to user welcome page
|
||||
console.info(`Success - response ${JSON.stringify(token)}`)
|
||||
} else {
|
||||
} catch (e) {
|
||||
// TODO: notify the user
|
||||
const err = await resp.text()
|
||||
console.error(`Failure - ${err}`)
|
||||
console.error(`Failure - ${e}`)
|
||||
}
|
||||
}
|
||||
|
3
src/jobs-jobs-jobs/src/views/citizen/Welcome.vue
Normal file
3
src/jobs-jobs-jobs/src/views/citizen/Welcome.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<div>Welcome</div>
|
||||
</template>
|
Loading…
Reference in New Issue
Block a user