progress
server is launchable and passkey support works
This commit is contained in:
parent
ee172d84a8
commit
c572066571
21 changed files with 857 additions and 40 deletions
26
authentication-flow.md
Normal file
26
authentication-flow.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Plan for how authentication will work
|
||||||
|
|
||||||
|
## Frontend auth
|
||||||
|
|
||||||
|
### Registration
|
||||||
|
|
||||||
|
1. Send username to registration endpoint
|
||||||
|
2. Get webauthn options
|
||||||
|
3. Perform webauthn check (selecting and confirming passkey)
|
||||||
|
4. Server verifies response
|
||||||
|
5. Minimal account ready for login
|
||||||
|
|
||||||
|
### Login
|
||||||
|
|
||||||
|
1. Send username to login endpoint
|
||||||
|
2. Error out if user doesn't exist
|
||||||
|
3. Get webauthn options from response
|
||||||
|
4. Get passkey response
|
||||||
|
5. Send response to Server
|
||||||
|
6. Server checks and replies with session token
|
||||||
|
7. Frontend uses session token for authorisation of all requests afterwards
|
||||||
|
|
||||||
|
## api
|
||||||
|
|
||||||
|
1. Generate API token via frontend
|
||||||
|
2. Use api token for authorisation
|
0
cmd/test/example/example.txt
Normal file
0
cmd/test/example/example.txt
Normal file
13
cmd/test/main.go
Normal file
13
cmd/test/main.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed example
|
||||||
|
var fs embed.FS
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println(fs.ReadDir("example"))
|
||||||
|
}
|
BIN
cmd/test/test
Executable file
BIN
cmd/test/test
Executable file
Binary file not shown.
|
@ -2,7 +2,6 @@
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: docker.io/postgres:16.4
|
image: docker.io/postgres:16.4
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: linstrom
|
POSTGRES_PASSWORD: linstrom
|
||||||
POSTGRES_USER: linstrom
|
POSTGRES_USER: linstrom
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
|
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
|
||||||
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
|
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
|
||||||
*/
|
*/
|
||||||
"isTypeScriptProject": true
|
"isTypeScriptProject": true,
|
||||||
|
"pnpm": true
|
||||||
}
|
}
|
||||||
|
|
101
frontend-reactive/package-lock.json
generated
101
frontend-reactive/package-lock.json
generated
|
@ -73,6 +73,7 @@
|
||||||
"ember-page-title": "^8.2.3",
|
"ember-page-title": "^8.2.3",
|
||||||
"ember-qunit": "^8.1.0",
|
"ember-qunit": "^8.1.0",
|
||||||
"ember-resolver": "^11.0.1",
|
"ember-resolver": "^11.0.1",
|
||||||
|
"ember-simple-auth": "^6.1.0",
|
||||||
"ember-source": "~5.11.0",
|
"ember-source": "~5.11.0",
|
||||||
"ember-template-lint": "^5.13.0",
|
"ember-template-lint": "^5.13.0",
|
||||||
"ember-tooltips": "^3.6.0",
|
"ember-tooltips": "^3.6.0",
|
||||||
|
@ -163,6 +164,35 @@
|
||||||
"url": "https://opencollective.com/babel"
|
"url": "https://opencollective.com/babel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/eslint-parser": {
|
||||||
|
"version": "7.25.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.1.tgz",
|
||||||
|
"integrity": "sha512-Y956ghgTT4j7rKesabkh5WeqgSFZVFwaPR0IWFm7KFHFmmJ4afbG49SmfW4S+GyRPx0Dy5jxEWA5t0rpxfElWg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
|
||||||
|
"eslint-visitor-keys": "^2.1.0",
|
||||||
|
"semver": "^6.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10.13.0 || ^12.13.0 || >=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/core": "^7.11.0",
|
||||||
|
"eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/eslint-parser/node_modules/eslint-visitor-keys": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/generator": {
|
"node_modules/@babel/generator": {
|
||||||
"version": "7.25.6",
|
"version": "7.25.6",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz",
|
||||||
|
@ -5844,6 +5874,40 @@
|
||||||
"node": "12.* || >= 14"
|
"node": "12.* || >= 14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
|
||||||
|
"version": "5.1.1-v1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
|
||||||
|
"integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"eslint-scope": "5.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"esrecurse": "^4.3.0",
|
||||||
|
"estraverse": "^4.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
@ -15512,6 +15576,19 @@
|
||||||
"semver": "bin/semver"
|
"semver": "bin/semver"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ember-cookies": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ember-cookies/-/ember-cookies-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-6GaN0eEDZT9SEUSZBxWzZMlvxjcGKXFTJNjv30LVXTTOxozE5IBmIxiDAEq0udi0UpWUGHLYQBgnANn4jdll7w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@embroider/addon-shim": "^1.7.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ember-data": {
|
"node_modules/ember-data": {
|
||||||
"version": "5.3.8",
|
"version": "5.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/ember-data/-/ember-data-5.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/ember-data/-/ember-data-5.3.8.tgz",
|
||||||
|
@ -20172,6 +20249,30 @@
|
||||||
"node": "8.* || 10.* || >= 12"
|
"node": "8.* || 10.* || >= 12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ember-simple-auth": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ember-simple-auth/-/ember-simple-auth-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-LhOl7TrOKlqb+0a/5STOoTSncDNuPELuFZ9+1SLduVX7DtdQr8VOEAmB8UaOnG0clJ9Bj6E3SczhXGjqd718Lw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/eslint-parser": "^7.24.7",
|
||||||
|
"@ember/test-waiters": "^3",
|
||||||
|
"@embroider/addon-shim": "^1.0.0",
|
||||||
|
"@embroider/macros": "^1.0.0",
|
||||||
|
"ember-cli-is-package-missing": "^1.0.0",
|
||||||
|
"ember-cookies": "^1.0.0",
|
||||||
|
"silent-error": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@ember/test-helpers": ">= 3 || > 2.7"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@ember/test-helpers": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ember-source": {
|
"node_modules/ember-source": {
|
||||||
"version": "5.11.0",
|
"version": "5.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/ember-source/-/ember-source-5.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/ember-source/-/ember-source-5.11.0.tgz",
|
||||||
|
|
|
@ -87,6 +87,7 @@
|
||||||
"ember-page-title": "^8.2.3",
|
"ember-page-title": "^8.2.3",
|
||||||
"ember-qunit": "^8.1.0",
|
"ember-qunit": "^8.1.0",
|
||||||
"ember-resolver": "^11.0.1",
|
"ember-resolver": "^11.0.1",
|
||||||
|
"ember-simple-auth": "^6.1.0",
|
||||||
"ember-source": "~5.11.0",
|
"ember-source": "~5.11.0",
|
||||||
"ember-template-lint": "^5.13.0",
|
"ember-template-lint": "^5.13.0",
|
||||||
"ember-tooltips": "^3.6.0",
|
"ember-tooltips": "^3.6.0",
|
||||||
|
|
16
go.mod
16
go.mod
|
@ -6,11 +6,13 @@ toolchain go1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.4.0
|
github.com/BurntSushi/toml v1.4.0
|
||||||
|
github.com/aws/aws-sdk-go v1.55.5
|
||||||
github.com/dgraph-io/ristretto v0.1.1
|
github.com/dgraph-io/ristretto v0.1.1
|
||||||
github.com/eko/gocache/lib/v4 v4.1.6
|
github.com/eko/gocache/lib/v4 v4.1.6
|
||||||
github.com/eko/gocache/store/redis/v4 v4.2.2
|
github.com/eko/gocache/store/redis/v4 v4.2.2
|
||||||
github.com/eko/gocache/store/ristretto/v4 v4.2.2
|
github.com/eko/gocache/store/ristretto/v4 v4.2.2
|
||||||
github.com/glebarez/sqlite v1.11.0
|
github.com/gabriel-vasile/mimetype v1.4.5
|
||||||
|
github.com/gen2brain/avif v0.3.2
|
||||||
github.com/go-webauthn/webauthn v0.11.2
|
github.com/go-webauthn/webauthn v0.11.2
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/mstarongithub/passkey v0.0.0-20240817142622-de6912c8303e
|
github.com/mstarongithub/passkey v0.0.0-20240817142622-de6912c8303e
|
||||||
|
@ -19,6 +21,7 @@ require (
|
||||||
github.com/xhit/go-simple-mail/v2 v2.16.0
|
github.com/xhit/go-simple-mail/v2 v2.16.0
|
||||||
gitlab.com/mstarongitlab/goap v1.1.0
|
gitlab.com/mstarongitlab/goap v1.1.0
|
||||||
gitlab.com/mstarongitlab/goutils v1.3.0
|
gitlab.com/mstarongitlab/goutils v1.3.0
|
||||||
|
golang.org/x/image v0.20.0
|
||||||
gorm.io/driver/postgres v1.5.7
|
gorm.io/driver/postgres v1.5.7
|
||||||
gorm.io/gorm v1.25.10
|
gorm.io/gorm v1.25.10
|
||||||
)
|
)
|
||||||
|
@ -43,15 +46,11 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go v1.55.5 // indirect
|
|
||||||
github.com/datainq/xml-date-time v0.0.0-20170820214645-2292f08baa38 // indirect
|
github.com/datainq/xml-date-time v0.0.0-20170820214645-2292f08baa38 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/ebitengine/purego v0.7.1 // indirect
|
github.com/ebitengine/purego v0.7.1 // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
|
||||||
github.com/gen2brain/avif v0.3.2 // indirect
|
|
||||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
|
||||||
github.com/go-test/deep v1.1.1 // indirect
|
github.com/go-test/deep v1.1.1 // indirect
|
||||||
github.com/go-webauthn/x v0.1.14 // indirect
|
github.com/go-webauthn/x v0.1.14 // indirect
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||||
|
@ -67,21 +66,16 @@ require (
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/piprate/json-gold v0.5.0 // indirect
|
github.com/piprate/json-gold v0.5.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
||||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
|
github.com/rs/xid v1.5.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/stretchr/testify v1.9.0 // indirect
|
github.com/stretchr/testify v1.9.0 // indirect
|
||||||
github.com/tetratelabs/wazero v1.7.3 // indirect
|
github.com/tetratelabs/wazero v1.7.3 // indirect
|
||||||
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
|
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
golang.org/x/crypto v0.26.0 // indirect
|
golang.org/x/crypto v0.26.0 // indirect
|
||||||
golang.org/x/image v0.20.0 // indirect
|
|
||||||
golang.org/x/net v0.27.0 // indirect
|
golang.org/x/net v0.27.0 // indirect
|
||||||
golang.org/x/sys v0.23.0 // indirect
|
golang.org/x/sys v0.23.0 // indirect
|
||||||
golang.org/x/text v0.18.0 // indirect
|
golang.org/x/text v0.18.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/libc v1.22.5 // indirect
|
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
|
||||||
modernc.org/memory v1.5.0 // indirect
|
|
||||||
modernc.org/sqlite v1.23.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
22
go.sum
22
go.sum
|
@ -93,10 +93,6 @@ github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||||
github.com/gen2brain/avif v0.3.2 h1:XUR0CBl5n4ISFJE8/pc1RMEKt5KUVoW8InctN+M7+DQ=
|
github.com/gen2brain/avif v0.3.2 h1:XUR0CBl5n4ISFJE8/pc1RMEKt5KUVoW8InctN+M7+DQ=
|
||||||
github.com/gen2brain/avif v0.3.2/go.mod h1:tdL2sV6oOJXBZZvT5iP55VEM1X2c3/yJmYKMJTl8fXg=
|
github.com/gen2brain/avif v0.3.2/go.mod h1:tdL2sV6oOJXBZZvT5iP55VEM1X2c3/yJmYKMJTl8fXg=
|
||||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
|
||||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
|
||||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
|
||||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
|
@ -175,8 +171,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
@ -197,6 +191,7 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
@ -273,12 +268,10 @@ github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5
|
||||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||||
github.com/redis/go-redis/v9 v9.0.2 h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE=
|
github.com/redis/go-redis/v9 v9.0.2 h1:BA426Zqe/7r56kCcvxYLWe1mkaz71LKF77GwgFzSxfE=
|
||||||
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
|
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
|
@ -467,8 +460,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
|
||||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
|
||||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -609,6 +600,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
@ -624,14 +616,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
|
||||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
|
||||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
|
||||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
|
||||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
|
||||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
|
||||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
|
51
main.go
51
main.go
|
@ -2,18 +2,32 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
|
"github.com/mstarongithub/passkey"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gitlab.com/mstarongitlab/linstrom/ap"
|
|
||||||
|
// "gitlab.com/mstarongitlab/linstrom/ap"
|
||||||
"gitlab.com/mstarongitlab/linstrom/config"
|
"gitlab.com/mstarongitlab/linstrom/config"
|
||||||
|
"gitlab.com/mstarongitlab/linstrom/server"
|
||||||
"gitlab.com/mstarongitlab/linstrom/storage"
|
"gitlab.com/mstarongitlab/linstrom/storage"
|
||||||
"gitlab.com/mstarongitlab/linstrom/storage/cache"
|
"gitlab.com/mstarongitlab/linstrom/storage/cache"
|
||||||
|
"gitlab.com/mstarongitlab/linstrom/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed frontend-reactive/dist/* frontend-reactive/dist/assets
|
||||||
|
var reactiveFS embed.FS
|
||||||
|
|
||||||
|
//go:embed frontend-noscript
|
||||||
|
var nojsFS embed.FS
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
setLogger()
|
setLogger()
|
||||||
setLogLevel()
|
setLogLevel()
|
||||||
|
@ -23,11 +37,11 @@ func main() {
|
||||||
Str("config-file", *flagConfigFile).
|
Str("config-file", *flagConfigFile).
|
||||||
Msg("Failed to read config and couldn't write default")
|
Msg("Failed to read config and couldn't write default")
|
||||||
}
|
}
|
||||||
res, err := ap.GetAccountWebfinger("@aufricus_athudath@activitypub.academy")
|
// res, err := ap.GetAccountWebfinger("@aufricus_athudath@activitypub.academy")
|
||||||
log.Info().
|
// log.Info().
|
||||||
Err(err).
|
// Err(err).
|
||||||
Any("webfinger", res).
|
// Any("webfinger", res).
|
||||||
Msg("Webfinger request result for @aufricus_athudath@activitypub.academy")
|
// Msg("Webfinger request result for @aufricus_athudath@activitypub.academy")
|
||||||
storageCache, err := cache.NewCache(
|
storageCache, err := cache.NewCache(
|
||||||
config.GlobalConfig.Storage.MaxInMemoryCacheSize,
|
config.GlobalConfig.Storage.MaxInMemoryCacheSize,
|
||||||
config.GlobalConfig.Storage.RedisUrl,
|
config.GlobalConfig.Storage.RedisUrl,
|
||||||
|
@ -48,7 +62,30 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to setup storage")
|
log.Fatal().Err(err).Msg("Failed to setup storage")
|
||||||
}
|
}
|
||||||
_ = store
|
|
||||||
|
pkey, err := passkey.New(passkey.Config{
|
||||||
|
WebauthnConfig: &webauthn.Config{
|
||||||
|
RPDisplayName: "Linstrom",
|
||||||
|
RPID: "localhost",
|
||||||
|
RPOrigins: []string{"http://localhost:8000"},
|
||||||
|
},
|
||||||
|
UserStore: store,
|
||||||
|
SessionStore: store,
|
||||||
|
SessionMaxAge: time.Hour * 24,
|
||||||
|
}, passkey.WithLogger(&util.ZerologWrapper{}))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to setup passkey support")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(nojsFS.ReadDir("frontend-noscript"))
|
||||||
|
|
||||||
|
server := server.NewServer(
|
||||||
|
store,
|
||||||
|
pkey,
|
||||||
|
util.NewFSWrapper(reactiveFS, "frontend-reactive/dist/", true),
|
||||||
|
util.NewFSWrapper(nojsFS, "frontend-noscript/", true),
|
||||||
|
)
|
||||||
|
server.Start(":8000")
|
||||||
// TODO: Set up media server
|
// TODO: Set up media server
|
||||||
// TODO: Set up queues
|
// TODO: Set up queues
|
||||||
// TODO: Set up http server
|
// TODO: Set up http server
|
||||||
|
|
330
pk-auth/index.es5.umd.min.js
vendored
Normal file
330
pk-auth/index.es5.umd.min.js
vendored
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
/* [@simplewebauthn/browser@10.0.0] */
|
||||||
|
!(function(e, t) {
|
||||||
|
"object" == typeof exports && "undefined" != typeof module
|
||||||
|
? t(exports)
|
||||||
|
: "function" == typeof define && define.amd
|
||||||
|
? define(["exports"], t)
|
||||||
|
: t(
|
||||||
|
((e =
|
||||||
|
"undefined" != typeof globalThis
|
||||||
|
? globalThis
|
||||||
|
: e || self).SimpleWebAuthnBrowser = {}),
|
||||||
|
);
|
||||||
|
})(this, function(e) {
|
||||||
|
"use strict";
|
||||||
|
function t(e) {
|
||||||
|
const t = new Uint8Array(e);
|
||||||
|
let r = "";
|
||||||
|
for (const e of t) r += String.fromCharCode(e);
|
||||||
|
return btoa(r).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
||||||
|
}
|
||||||
|
function r(e) {
|
||||||
|
console.trace(e);
|
||||||
|
const t = e.replace(/-/g, "+").replace(/_/g, "/"),
|
||||||
|
r = (4 - (t.length % 4)) % 4,
|
||||||
|
n = t.padEnd(t.length + r, "="),
|
||||||
|
o = atob(n),
|
||||||
|
i = new ArrayBuffer(o.length),
|
||||||
|
a = new Uint8Array(i);
|
||||||
|
for (let e = 0; e < o.length; e++) a[e] = o.charCodeAt(e);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
function n() {
|
||||||
|
return (
|
||||||
|
void 0 !== window?.PublicKeyCredential &&
|
||||||
|
"function" == typeof window.PublicKeyCredential
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function o(e) {
|
||||||
|
const { id: t } = e;
|
||||||
|
return { ...e, id: r(t), transports: e.transports };
|
||||||
|
}
|
||||||
|
function i(e) {
|
||||||
|
return (
|
||||||
|
"localhost" === e || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
class a extends Error {
|
||||||
|
constructor({ message: e, code: t, cause: r, name: n }) {
|
||||||
|
super(e, { cause: r }), (this.name = n ?? r.name), (this.code = t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const s = new (class {
|
||||||
|
createNewAbortSignal() {
|
||||||
|
if (this.controller) {
|
||||||
|
const e = new Error(
|
||||||
|
"Cancelling existing WebAuthn API call for new one",
|
||||||
|
);
|
||||||
|
(e.name = "AbortError"), this.controller.abort(e);
|
||||||
|
}
|
||||||
|
const e = new AbortController();
|
||||||
|
return (this.controller = e), e.signal;
|
||||||
|
}
|
||||||
|
cancelCeremony() {
|
||||||
|
if (this.controller) {
|
||||||
|
const e = new Error("Manually cancelling existing WebAuthn API call");
|
||||||
|
(e.name = "AbortError"),
|
||||||
|
this.controller.abort(e),
|
||||||
|
(this.controller = void 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
c = ["cross-platform", "platform"];
|
||||||
|
function l(e) {
|
||||||
|
if (e && !(c.indexOf(e) < 0)) return e;
|
||||||
|
}
|
||||||
|
function u(e, t) {
|
||||||
|
console.warn(
|
||||||
|
`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${e}. You should report this error to them.\n`,
|
||||||
|
t,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function d() {
|
||||||
|
if (!n()) return new Promise((e) => e(!1));
|
||||||
|
const e = window.PublicKeyCredential;
|
||||||
|
return void 0 === e.isConditionalMediationAvailable
|
||||||
|
? new Promise((e) => e(!1))
|
||||||
|
: e.isConditionalMediationAvailable();
|
||||||
|
}
|
||||||
|
(e.WebAuthnAbortService = s),
|
||||||
|
(e.WebAuthnError = a),
|
||||||
|
(e.base64URLStringToBuffer = r),
|
||||||
|
(e.browserSupportsWebAuthn = n),
|
||||||
|
(e.browserSupportsWebAuthnAutofill = d),
|
||||||
|
(e.bufferToBase64URLString = t),
|
||||||
|
(e.platformAuthenticatorIsAvailable = function() {
|
||||||
|
return n()
|
||||||
|
? PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
|
||||||
|
: new Promise((e) => e(!1));
|
||||||
|
}),
|
||||||
|
(e.startAuthentication = async function(e, c = !1) {
|
||||||
|
if (!n()) throw new Error("WebAuthn is not supported in this browser");
|
||||||
|
let u;
|
||||||
|
0 !== e.allowCredentials?.length && (u = e.allowCredentials?.map(o));
|
||||||
|
const h = { ...e, challenge: r(e.challenge), allowCredentials: u },
|
||||||
|
f = {};
|
||||||
|
if (c) {
|
||||||
|
if (!(await d()))
|
||||||
|
throw Error("Browser does not support WebAuthn autofill");
|
||||||
|
if (
|
||||||
|
document.querySelectorAll("input[autocomplete$='webauthn']").length <
|
||||||
|
1
|
||||||
|
)
|
||||||
|
throw Error(
|
||||||
|
'No <input> with "webauthn" as the only or last value in its `autocomplete` attribute was detected',
|
||||||
|
);
|
||||||
|
(f.mediation = "conditional"), (h.allowCredentials = []);
|
||||||
|
}
|
||||||
|
let p;
|
||||||
|
(f.publicKey = h), (f.signal = s.createNewAbortSignal());
|
||||||
|
try {
|
||||||
|
p = await navigator.credentials.get(f);
|
||||||
|
} catch (e) {
|
||||||
|
throw (function({ error: e, options: t }) {
|
||||||
|
const { publicKey: r } = t;
|
||||||
|
if (!r)
|
||||||
|
throw Error("options was missing required publicKey property");
|
||||||
|
if ("AbortError" === e.name) {
|
||||||
|
if (t.signal instanceof AbortSignal)
|
||||||
|
return new a({
|
||||||
|
message: "Authentication ceremony was sent an abort signal",
|
||||||
|
code: "ERROR_CEREMONY_ABORTED",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if ("NotAllowedError" === e.name)
|
||||||
|
return new a({
|
||||||
|
message: e.message,
|
||||||
|
code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
if ("SecurityError" === e.name) {
|
||||||
|
const t = window.location.hostname;
|
||||||
|
if (!i(t))
|
||||||
|
return new a({
|
||||||
|
message: `${window.location.hostname} is an invalid domain`,
|
||||||
|
code: "ERROR_INVALID_DOMAIN",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
if (r.rpId !== t)
|
||||||
|
return new a({
|
||||||
|
message: `The RP ID "${r.rpId}" is invalid for this domain`,
|
||||||
|
code: "ERROR_INVALID_RP_ID",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
} else if ("UnknownError" === e.name)
|
||||||
|
return new a({
|
||||||
|
message:
|
||||||
|
"The authenticator was unable to process the specified options, or could not create a new assertion signature",
|
||||||
|
code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
})({ error: e, options: f });
|
||||||
|
}
|
||||||
|
if (!p) throw new Error("Authentication was not completed");
|
||||||
|
const { id: R, rawId: w, response: E, type: g } = p;
|
||||||
|
let A;
|
||||||
|
return (
|
||||||
|
E.userHandle && (A = t(E.userHandle)),
|
||||||
|
{
|
||||||
|
id: R,
|
||||||
|
rawId: t(w),
|
||||||
|
response: {
|
||||||
|
authenticatorData: t(E.authenticatorData),
|
||||||
|
clientDataJSON: t(E.clientDataJSON),
|
||||||
|
signature: t(E.signature),
|
||||||
|
userHandle: A,
|
||||||
|
},
|
||||||
|
type: g,
|
||||||
|
clientExtensionResults: p.getClientExtensionResults(),
|
||||||
|
authenticatorAttachment: l(p.authenticatorAttachment),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
(e.startRegistration = async function(e) {
|
||||||
|
if (!n()) throw new Error("WebAuthn is not supported in this browser");
|
||||||
|
console.log("StartRegVal", e);
|
||||||
|
const c = {
|
||||||
|
publicKey: {
|
||||||
|
...e,
|
||||||
|
challenge: r(e.challenge),
|
||||||
|
user: { ...e.user, id: r(e.user.id) },
|
||||||
|
excludeCredentials: e.excludeCredentials?.map(o),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let d;
|
||||||
|
c.signal = s.createNewAbortSignal();
|
||||||
|
try {
|
||||||
|
d = await navigator.credentials.create(c);
|
||||||
|
} catch (e) {
|
||||||
|
throw (function({ error: e, options: t }) {
|
||||||
|
const { publicKey: r } = t;
|
||||||
|
if (!r)
|
||||||
|
throw Error("options was missing required publicKey property");
|
||||||
|
if ("AbortError" === e.name) {
|
||||||
|
if (t.signal instanceof AbortSignal)
|
||||||
|
return new a({
|
||||||
|
message: "Registration ceremony was sent an abort signal",
|
||||||
|
code: "ERROR_CEREMONY_ABORTED",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
} else if ("ConstraintError" === e.name) {
|
||||||
|
if (!0 === r.authenticatorSelection?.requireResidentKey)
|
||||||
|
return new a({
|
||||||
|
message:
|
||||||
|
"Discoverable credentials were required but no available authenticator supported it",
|
||||||
|
code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
if ("required" === r.authenticatorSelection?.userVerification)
|
||||||
|
return new a({
|
||||||
|
message:
|
||||||
|
"User verification was required but no available authenticator supported it",
|
||||||
|
code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if ("InvalidStateError" === e.name)
|
||||||
|
return new a({
|
||||||
|
message: "The authenticator was previously registered",
|
||||||
|
code: "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
if ("NotAllowedError" === e.name)
|
||||||
|
return new a({
|
||||||
|
message: e.message,
|
||||||
|
code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
if ("NotSupportedError" === e.name)
|
||||||
|
return 0 ===
|
||||||
|
r.pubKeyCredParams.filter((e) => "public-key" === e.type).length
|
||||||
|
? new a({
|
||||||
|
message:
|
||||||
|
'No entry in pubKeyCredParams was of type "public-key"',
|
||||||
|
code: "ERROR_MALFORMED_PUBKEYCREDPARAMS",
|
||||||
|
cause: e,
|
||||||
|
})
|
||||||
|
: new a({
|
||||||
|
message:
|
||||||
|
"No available authenticator supported any of the specified pubKeyCredParams algorithms",
|
||||||
|
code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
if ("SecurityError" === e.name) {
|
||||||
|
const t = window.location.hostname;
|
||||||
|
if (!i(t))
|
||||||
|
return new a({
|
||||||
|
message: `${window.location.hostname} is an invalid domain`,
|
||||||
|
code: "ERROR_INVALID_DOMAIN",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
if (r.rp.id !== t)
|
||||||
|
return new a({
|
||||||
|
message: `The RP ID "${r.rp.id}" is invalid for this domain`,
|
||||||
|
code: "ERROR_INVALID_RP_ID",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
} else if ("TypeError" === e.name) {
|
||||||
|
if (r.user.id.byteLength < 1 || r.user.id.byteLength > 64)
|
||||||
|
return new a({
|
||||||
|
message: "User ID was not between 1 and 64 characters",
|
||||||
|
code: "ERROR_INVALID_USER_ID_LENGTH",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
} else if ("UnknownError" === e.name)
|
||||||
|
return new a({
|
||||||
|
message:
|
||||||
|
"The authenticator was unable to process the specified options, or could not create a new credential",
|
||||||
|
code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
|
||||||
|
cause: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
})({ error: e, options: c });
|
||||||
|
}
|
||||||
|
if (!d) throw new Error("Registration was not completed");
|
||||||
|
const { id: h, rawId: f, response: p, type: R } = d;
|
||||||
|
let w, E, g, A;
|
||||||
|
if (
|
||||||
|
("function" == typeof p.getTransports && (w = p.getTransports()),
|
||||||
|
"function" == typeof p.getPublicKeyAlgorithm)
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
E = p.getPublicKeyAlgorithm();
|
||||||
|
} catch (e) {
|
||||||
|
u("getPublicKeyAlgorithm()", e);
|
||||||
|
}
|
||||||
|
if ("function" == typeof p.getPublicKey)
|
||||||
|
try {
|
||||||
|
const e = p.getPublicKey();
|
||||||
|
null !== e && (g = t(e));
|
||||||
|
} catch (e) {
|
||||||
|
u("getPublicKey()", e);
|
||||||
|
}
|
||||||
|
if ("function" == typeof p.getAuthenticatorData)
|
||||||
|
try {
|
||||||
|
A = t(p.getAuthenticatorData());
|
||||||
|
} catch (e) {
|
||||||
|
u("getAuthenticatorData()", e);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: h,
|
||||||
|
rawId: t(f),
|
||||||
|
response: {
|
||||||
|
attestationObject: t(p.attestationObject),
|
||||||
|
clientDataJSON: t(p.clientDataJSON),
|
||||||
|
transports: w,
|
||||||
|
publicKeyAlgorithm: E,
|
||||||
|
publicKey: g,
|
||||||
|
authenticatorData: A,
|
||||||
|
},
|
||||||
|
type: R,
|
||||||
|
clientExtensionResults: d.getClientExtensionResults(),
|
||||||
|
authenticatorAttachment: l(d.authenticatorAttachment),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
Object.defineProperty(e, "__esModule", { value: !0 });
|
||||||
|
});
|
110
pk-auth/script.js
Normal file
110
pk-auth/script.js
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
document.getElementById("registerButton").addEventListener("click", register);
|
||||||
|
document.getElementById("loginButton").addEventListener("click", login);
|
||||||
|
|
||||||
|
function showMessage(message, isError = false) {
|
||||||
|
const messageElement = document.getElementById("message");
|
||||||
|
messageElement.textContent = message;
|
||||||
|
messageElement.style.color = isError ? "red" : "green";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function register() {
|
||||||
|
// Retrieve the username from the input field
|
||||||
|
const username = document.getElementById("username").value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get registration options from your server. Here, we also receive the challenge.
|
||||||
|
const response = await fetch("/webauthn/passkey/registerBegin", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username: username }),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if the registration options are ok.
|
||||||
|
if (!response.ok) {
|
||||||
|
const msg = await response.json();
|
||||||
|
throw new Error(
|
||||||
|
"User already exists or failed to get registration options from server: " +
|
||||||
|
msg,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the registration options to JSON.
|
||||||
|
const options = await response.json();
|
||||||
|
console.log("registration start", options);
|
||||||
|
|
||||||
|
// This triggers the browser to display the passkey / WebAuthn modal (e.g. Face ID, Touch ID, Windows Hello).
|
||||||
|
// A new attestation is created. This also means a new public-private-key pair is created.
|
||||||
|
const attestationResponse = await SimpleWebAuthnBrowser.startRegistration(
|
||||||
|
options.publicKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Attempting to complete registration", attestationResponse);
|
||||||
|
// Send attestationResponse back to server for verification and storage.
|
||||||
|
const verificationResponse = await fetch(
|
||||||
|
"/webauthn/passkey/registerFinish",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
credentials: "same-origin",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(attestationResponse),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const msg = await verificationResponse.json();
|
||||||
|
if (verificationResponse.ok) {
|
||||||
|
showMessage(msg, false);
|
||||||
|
} else {
|
||||||
|
showMessage(msg, true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showMessage("Error: " + error.message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login() {
|
||||||
|
// Retrieve the username from the input field
|
||||||
|
const username = document.getElementById("username").value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get login options from your server. Here, we also receive the challenge.
|
||||||
|
const response = await fetch("/webauthn/passkey/loginBegin", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username: username }),
|
||||||
|
});
|
||||||
|
// Check if the login options are ok.
|
||||||
|
if (!response.ok) {
|
||||||
|
const msg = await response.json();
|
||||||
|
throw new Error("Failed to get login options from server: " + msg);
|
||||||
|
}
|
||||||
|
// Convert the login options to JSON.
|
||||||
|
const options = await response.json();
|
||||||
|
|
||||||
|
// This triggers the browser to display the passkey / WebAuthn modal (e.g. Face ID, Touch ID, Windows Hello).
|
||||||
|
// A new assertionResponse is created. This also means that the challenge has been signed.
|
||||||
|
const assertionResponse = await SimpleWebAuthnBrowser.startAuthentication(
|
||||||
|
options.publicKey,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send assertionResponse back to server for verification.
|
||||||
|
const verificationResponse = await fetch("/webauthn/passkey/loginFinish", {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "same-origin",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(assertionResponse),
|
||||||
|
});
|
||||||
|
|
||||||
|
const msg = await verificationResponse.json();
|
||||||
|
if (verificationResponse.ok) {
|
||||||
|
showMessage(msg, false);
|
||||||
|
} else {
|
||||||
|
showMessage(msg, true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showMessage("Error: " + error.message, true);
|
||||||
|
}
|
||||||
|
}
|
65
pk-auth/test.html
Normal file
65
pk-auth/test.html
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Passkey</title>
|
||||||
|
<link href="bootstrap.min.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">Passkey</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
|
||||||
|
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item active">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/private">Private</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav ml-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="https://github.com/egregors/passkey" target="_blank">
|
||||||
|
<img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" width="30"
|
||||||
|
height="30" alt="GitHub logo">
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container d-flex justify-content-center align-items-center vh-100">
|
||||||
|
<div class="bg-light p-5 rounded w-50">
|
||||||
|
<h1 class="mb-4 text-center">🔑 Passkey</h1>
|
||||||
|
<div class="text-center" id="message"></div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="text" class="form-control" id="username" placeholder="username">
|
||||||
|
</div>
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<button class="btn btn-primary w-100" id="registerButton">Register</button>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<button class="btn btn-primary w-100" id="loginButton">Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="index.es5.umd.min.js"></script>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
1
server/auth.go
Normal file
1
server/auth.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package server
|
53
server/middlewares.go
Normal file
53
server/middlewares.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/hlog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HandlerBuilder func(http.Handler) http.Handler
|
||||||
|
|
||||||
|
func ChainMiddlewares(base http.Handler, links ...HandlerBuilder) http.Handler {
|
||||||
|
slices.Reverse(links)
|
||||||
|
for _, f := range links {
|
||||||
|
base = f(base)
|
||||||
|
}
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextValsMiddleware(pairs map[any]any) HandlerBuilder {
|
||||||
|
return func(h http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
for key, val := range pairs {
|
||||||
|
ctx = context.WithValue(ctx, key, val)
|
||||||
|
}
|
||||||
|
newRequest := r.WithContext(ctx)
|
||||||
|
h.ServeHTTP(w, newRequest)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoggingMiddleware(handler http.Handler) http.Handler {
|
||||||
|
return ChainMiddlewares(handler,
|
||||||
|
hlog.NewHandler(log.Logger),
|
||||||
|
hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
|
||||||
|
hlog.FromRequest(r).Info().
|
||||||
|
Str("method", r.Method).
|
||||||
|
Stringer("url", r.URL).
|
||||||
|
Int("status", status).
|
||||||
|
Int("size", size).
|
||||||
|
Dur("duration", duration).
|
||||||
|
Send()
|
||||||
|
}),
|
||||||
|
hlog.RemoteAddrHandler("ip"),
|
||||||
|
hlog.UserAgentHandler("user_agent"),
|
||||||
|
hlog.RefererHandler("referer"),
|
||||||
|
hlog.RequestIDHandler("req_id", "Request-Id"),
|
||||||
|
)
|
||||||
|
}
|
41
server/server.go
Normal file
41
server/server.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/mstarongithub/passkey"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"gitlab.com/mstarongitlab/linstrom/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
store *storage.Storage
|
||||||
|
router http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(store *storage.Storage, pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) *Server {
|
||||||
|
handler := buildRootHandler(pkey, reactiveFS, staticFS)
|
||||||
|
handler = ChainMiddlewares(handler, LoggingMiddleware, ContextValsMiddleware(map[any]any{}))
|
||||||
|
return &Server{
|
||||||
|
store: store,
|
||||||
|
router: handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildRootHandler(pkey *passkey.Passkey, reactiveFS, staticFS fs.FS) http.Handler {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
pkey.MountRoutes(mux, "/webauthn/")
|
||||||
|
mux.Handle("/", http.FileServerFS(reactiveFS))
|
||||||
|
mux.Handle("/nojs/", http.StripPrefix("/nojs", http.FileServerFS(staticFS)))
|
||||||
|
mux.Handle("/pk/", http.StripPrefix("/pk", http.FileServer(http.Dir("pk-auth"))))
|
||||||
|
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, true) })
|
||||||
|
|
||||||
|
return mux
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Start(addr string) {
|
||||||
|
log.Info().Str("addr", addr).Msg("Starting server")
|
||||||
|
http.ListenAndServe(addr, s.router)
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-webauthn/webauthn/webauthn"
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/mstarongithub/passkey"
|
"github.com/mstarongithub/passkey"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"gitlab.com/mstarongitlab/linstrom/ap"
|
"gitlab.com/mstarongitlab/linstrom/ap"
|
||||||
|
@ -197,12 +198,15 @@ func (s *Storage) NewEmptyAccount() (*Account, error) {
|
||||||
log.Debug().Msg("Creating new empty account")
|
log.Debug().Msg("Creating new empty account")
|
||||||
acc := Account{}
|
acc := Account{}
|
||||||
// Generate the 64 bit id for passkey and webauthn stuff
|
// Generate the 64 bit id for passkey and webauthn stuff
|
||||||
|
log.Debug().Msg("Creating webauthn id for new account")
|
||||||
data := make([]byte, 64)
|
data := make([]byte, 64)
|
||||||
c, err := rand.Read(data)
|
c, err := rand.Read(data)
|
||||||
for err != nil || c != len(data) || c < 64 {
|
for err != nil || c != len(data) || c < 64 {
|
||||||
data = make([]byte, 64)
|
data = make([]byte, 64)
|
||||||
c, err = rand.Read(data)
|
c, err = rand.Read(data)
|
||||||
}
|
}
|
||||||
|
log.Debug().Msg("Random webauthn id for new account created")
|
||||||
|
acc.ID = uuid.NewString()
|
||||||
acc.WebAuthnId = data
|
acc.WebAuthnId = data
|
||||||
acc.Followers = []string{}
|
acc.Followers = []string{}
|
||||||
acc.Tags = []string{}
|
acc.Tags = []string{}
|
||||||
|
@ -211,7 +215,8 @@ func (s *Storage) NewEmptyAccount() (*Account, error) {
|
||||||
acc.CustomFields = []uint{}
|
acc.CustomFields = []uint{}
|
||||||
acc.IdentifiesAs = []Being{}
|
acc.IdentifiesAs = []Being{}
|
||||||
acc.PasskeyCredentials = []webauthn.Credential{}
|
acc.PasskeyCredentials = []webauthn.Credential{}
|
||||||
res := s.db.Save(acc)
|
log.Debug().Any("account", &acc).Msg("Saving new account in db")
|
||||||
|
res := s.db.Save(&acc)
|
||||||
if res.Error != nil {
|
if res.Error != nil {
|
||||||
log.Error().Err(res.Error).Msg("Failed to safe new account")
|
log.Error().Err(res.Error).Msg("Failed to safe new account")
|
||||||
return nil, res.Error
|
return nil, res.Error
|
||||||
|
|
35
util/fswrapper.go
Normal file
35
util/fswrapper.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fix for go:embed file systems including the full path of the embedded files
|
||||||
|
// Adds a given string to the front of all requests
|
||||||
|
type FSWrapper struct {
|
||||||
|
wrapped fs.FS
|
||||||
|
toAdd string
|
||||||
|
log bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFSWrapper(wraps fs.FS, appends string, logAccess bool) *FSWrapper {
|
||||||
|
return &FSWrapper{
|
||||||
|
wrapped: wraps,
|
||||||
|
toAdd: appends,
|
||||||
|
log: logAccess,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FSWrapper) Open(name string) (fs.File, error) {
|
||||||
|
res, err := fs.wrapped.Open(fs.toAdd + name)
|
||||||
|
if fs.log {
|
||||||
|
log.Debug().
|
||||||
|
Str("prefix", fs.toAdd).
|
||||||
|
Str("filename", name).
|
||||||
|
Err(err).
|
||||||
|
Msg("fswrapper: File access result")
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
21
util/zerologPasskey.go
Normal file
21
util/zerologPasskey.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package util
|
||||||
|
|
||||||
|
import "github.com/rs/zerolog/log"
|
||||||
|
|
||||||
|
type ZerologWrapper struct{}
|
||||||
|
|
||||||
|
func (z *ZerologWrapper) Errorf(format string, args ...any) {
|
||||||
|
log.Error().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZerologWrapper) Debugf(format string, args ...any) {
|
||||||
|
log.Debug().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZerologWrapper) Infof(format string, args ...any) {
|
||||||
|
log.Info().Msgf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZerologWrapper) Warnf(format string, args ...any) {
|
||||||
|
log.Warn().Msgf(format, args...)
|
||||||
|
}
|
Loading…
Reference in a new issue