Merge branch 'release/v0.2.7'
All checks were successful
/ docker (push) Successful in 1m39s

This commit is contained in:
Maxim Lebedev 2023-11-06 04:31:09 +06:00
commit cc798de6bc
Signed by: toby3d
GPG key ID: 1F14E25B7C119FC5
796 changed files with 123103 additions and 346605 deletions

View file

@ -0,0 +1,44 @@
---
on:
push:
branches:
- master
- develop
env:
DOCKER_REGISTRY: source.toby3d.me
jobs:
docker:
runs-on: ubuntu-latest
container:
image: catthehacker/ubuntu:act-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: https://gitea.com/actions/checkout@v3
- name: Set up QEMU
uses: https://gitea.com/docker/setup-qemu-action@v2
- name: Set up Docker BuildX
uses: https://gitea.com/docker/setup-buildx-action@v2
- name: Login to registry
uses: https://gitea.com/docker/login-action@v2
with:
registry: ${{ env.DOCKER_REGISTRY }}
username: ${{ gitea.repository_owner }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push
uses: https://gitea.com/docker/build-push-action@v4
env:
ACTIONS_RUNTIME_TOKEN: "" # See https://gitea.com/gitea/act_runner/issues/119
with:
context: .
file: ./build/Dockerfile
push: true
tags: ${{ env.DOCKER_REGISTRY }}/${{ gitea.repository }}:${{ gitea.ref_name }}

View file

@ -59,6 +59,15 @@ linters-settings:
- "i int"
- "r *http.Request"
- "w http.ResponseWriter"
depguard:
rules:
main:
allow:
- $gostd
- github.com/toby3d
- gitlab.com/toby3d
- source.toby3d.me
- golang.org
linters:
enable-all: true
disable:

28
build/Dockerfile Normal file
View file

@ -0,0 +1,28 @@
# syntax=docker/dockerfile:1
# docker build --rm -f build/Dockerfile -t source.toby3d.me/toby3d/auth .
# Build
FROM golang:alpine AS builder
WORKDIR /app
ENV CGO_ENABLED=0
ENV GOFLAGS="-mod=vendor -buildvcs=true"
COPY go.mod go.sum *.go ./
COPY internal ./internal/
COPY vendor ./vendor/
COPY web ./web/
RUN go build -o ./auth
# Run
FROM scratch
WORKDIR /
COPY --from=builder /app/auth /auth
EXPOSE 3000
ENTRYPOINT ["/auth"]

View file

@ -39,43 +39,65 @@ func init() {
}
var messageKeyToIndex = map[string]int{
"Allow": 4,
"%sProof of Key Code Exchange%s is a mechanism that protects against attackers in the middle hijacking your application's authentication process. You can still authorize this application without this protection, but you must independently verify the security of this connection. If you have any doubts - stop the process and contact the developers.": 4,
"Allow": 8,
"Authorize %s": 0,
"Authorize application": 1,
"Choose your scopes": 2,
"Deny": 3,
"Error": 6,
"How do I fix it?": 7,
"Recipient": 10,
"Resource": 11,
"Send": 12,
"Sign In": 8,
"TicketAuth": 9,
"version": 5,
"Deny": 7,
"Error": 10,
"How do I fix it?": 11,
"No scopes is requested: the application will only get your profile URL.": 6,
"Recipient": 14,
"Resource": 15,
"Scopes": 5,
"Send": 16,
"Sign In": 12,
"This client does not use %sPKCE%s!": 3,
"This client uses %sPKCE%s with the %s%s%s method.": 2,
"TicketAuth": 13,
"You will be redirected to %s%s%s": 9,
}
var enIndex = []uint32{ // 14 elements
0x00000000, 0x00000010, 0x00000026, 0x00000039,
0x0000003e, 0x00000044, 0x0000004c, 0x00000052,
0x00000063, 0x0000006b, 0x00000076, 0x00000080,
0x00000089, 0x0000008e,
} // Size: 80 bytes
var enIndex = []uint32{ // 18 elements
0x00000000, 0x00000010, 0x00000026, 0x00000067,
0x00000090, 0x000001f3, 0x000001fa, 0x00000242,
0x00000247, 0x0000024d, 0x00000277, 0x0000027d,
0x0000028e, 0x00000296, 0x000002a1, 0x000002ab,
0x000002b4, 0x000002b9,
} // Size: 96 bytes
const enData string = "" + // Size: 142 bytes
"\x02Authorize %[1]s\x02Authorize application\x02Choose your scopes\x02De" +
"ny\x02Allow\x02version\x02Error\x02How do I fix it?\x02Sign In\x02Ticket" +
"Auth\x02Recipient\x02Resource\x02Send"
const enData string = "" + // Size: 697 bytes
"\x02Authorize %[1]s\x02Authorize application\x02This client uses %[1]sPK" +
"CE%[2]s with the %[3]s%[4]s%[5]s method.\x02This client does not use %[1" +
"]sPKCE%[2]s!\x02%[1]sProof of Key Code Exchange%[2]s is a mechanism that" +
" protects against attackers in the middle hijacking your application's a" +
"uthentication process. You can still authorize this application without " +
"this protection, but you must independently verify the security of this " +
"connection. If you have any doubts - stop the process and contact the d" +
"evelopers.\x02Scopes\x02No scopes is requested: the application will onl" +
"y get your profile URL.\x02Deny\x02Allow\x02You will be redirected to %[" +
"1]s%[2]s%[3]s\x02Error\x02How do I fix it?\x02Sign In\x02TicketAuth\x02R" +
"ecipient\x02Resource\x02Send"
var ruIndex = []uint32{ // 14 elements
0x00000000, 0x0000001f, 0x0000004d, 0x0000008e,
0x0000009f, 0x000000b2, 0x000000bf, 0x000000cc,
0x000000ee, 0x000000f9, 0x00000104, 0x00000119,
0x00000126, 0x00000139,
} // Size: 80 bytes
var ruIndex = []uint32{ // 18 elements
0x00000000, 0x0000001f, 0x0000004d, 0x000000a1,
0x000000d8, 0x00000343, 0x00000352, 0x000003e9,
0x000003fa, 0x0000040d, 0x00000451, 0x0000045e,
0x00000480, 0x0000048b, 0x00000496, 0x000004ab,
0x000004b8, 0x000004cb,
} // Size: 96 bytes
const ruData string = "" + // Size: 313 bytes
"\x02Авторизовать %[1]s\x02Авторизовать приложение\x02Выбери предоставляе" +
"мые разрешения\x02Отказать\x02Разрешить\x02версия\x02Ошибка\x02Как испр" +
"авить это?\x02Войти\x02TicketAuth\x02Получатель\x02Ресурс\x02Отправить"
const ruData string = "" + // Size: 1227 bytes
"\x02Авторизовать %[1]s\x02Авторизовать приложение\x02Клиент использует %" +
"[1]sPKCE%[2]s с методом %[3]s%[4]s%[5]s.\x02Клиент не использует %[1]sPK" +
"CE%[2]s!\x02%[1]sProof of Key Code Exchange%[2]s это механизм, защищающи" +
"й от злоумышленников, перехватывающих процесс аутентификации вашего при" +
"ложения. Вы можете авторизовать данное приложение и без этой защиты, но" +
" при этом вы должны самостоятельно проверить безопасность этого соединен" +
"ия. Если у вас есть сомнения - прервите процесс и свяжитесь с разработч" +
"иками.\x02Области\x02Никакие разрешения не запрашиваются: приложение по" +
"лучит только URL вашего профиля.\x02Отказать\x02Разрешить\x02Вы будете " +
"перенаправлены на %[1]s%[2]s%[3]s\x02Ошибка\x02Как исправить это?\x02Во" +
"йти\x02TicketAuth\x02Получатель\x02Ресурс\x02Отправить"
// Total table size 615 bytes (0KiB); checksum: 66FB60EC
// Total table size 2116 bytes (2KiB); checksum: 848DD07E

View file

@ -1,4 +1,4 @@
# auth [![Build Status](https://drone.toby3d.me/api/badges/toby3d/auth/status.svg)](https://drone.toby3d.me/toby3d/auth)
# auth
> Personal [IndieAuth](https://indieauth.net/source/) server.

65
go.mod
View file

@ -1,60 +1,57 @@
module source.toby3d.me/toby3d/auth
go 1.19
go 1.21
require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/brianvoe/gofakeit/v6 v6.20.2
github.com/caarlos0/env/v7 v7.1.0
github.com/go-logfmt/logfmt v0.6.0
github.com/goccy/go-json v0.10.1
github.com/google/go-cmp v0.5.9
github.com/brianvoe/gofakeit/v6 v6.24.0
github.com/caarlos0/env/v9 v9.0.0
github.com/goccy/go-json v0.10.2
github.com/google/go-cmp v0.6.0
github.com/jmoiron/sqlx v1.3.5
github.com/lestrrat-go/jwx/v2 v2.0.8
github.com/lestrrat-go/jwx/v2 v2.0.16
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
github.com/valyala/fasttemplate v1.2.2
github.com/valyala/quicktemplate v1.7.0
go.etcd.io/bbolt v1.3.7
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0
golang.org/x/text v0.8.0
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
inet.af/netaddr v0.0.0-20220811202034-502d2d690317
modernc.org/sqlite v1.21.0
source.toby3d.me/toby3d/form v0.3.0
go.etcd.io/bbolt v1.3.8
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/text v0.13.0
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a
modernc.org/sqlite v1.27.0
source.toby3d.me/toby3d/form v0.4.0
willnorris.com/go/microformats v1.2.0
)
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.16.3 // indirect
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.4 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/lib/pq v1.10.6 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.45.0 // indirect
go4.org/intern v0.0.0-20230205224052-192e9f60865c // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/tools v0.7.0 // indirect
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/mod v0.13.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/tools v0.14.0 // indirect
lukechampine.com/uint128 v1.3.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
modernc.org/libc v1.22.3 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/cc/v3 v3.41.0 // indirect
modernc.org/ccgo/v3 v3.16.15 // indirect
modernc.org/libc v1.29.0 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)

175
go.sum
View file

@ -2,122 +2,123 @@ github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20O
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/brianvoe/gofakeit/v6 v6.20.2 h1:FLloufuC7NcbHqDzVQ42CG9AKryS1gAGCRt8nQRsW+Y=
github.com/brianvoe/gofakeit/v6 v6.20.2/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
github.com/brianvoe/gofakeit/v6 v6.24.0 h1:74yq7RRz/noddscZHRS2T84oHZisW9muwbb8sRnU52A=
github.com/brianvoe/gofakeit/v6 v6.24.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8=
github.com/caarlos0/env/v9 v9.0.0 h1:SI6JNsOA+y5gj9njpgybykATIylrRMklbs5ch6wO6pc=
github.com/caarlos0/env/v9 v9.0.0/go.mod h1:ye5mlCVMYh6tZ+vCgrs/B95sj88cg5Tlnc0XIzgZ020=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.1 h1:lEs5Ob+oOG/Ze199njvzHbhn6p9T+h64F5hRj69iTTo=
github.com/goccy/go-json v0.10.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8=
github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx/v2 v2.0.8 h1:jCFT8oc0hEDVjgUgsBy1F9cbjsjAVZSXNi7JaU9HR/Q=
github.com/lestrrat-go/jwx/v2 v2.0.8/go.mod h1:zLxnyv9rTlEvOUHbc48FAfIL8iYu2hHvIRaTFGc8mT0=
github.com/lestrrat-go/jwx/v2 v2.0.16 h1:TuH3dBkYTy2giQg/9D8f20znS3JtMRuQJ372boS3lWk=
github.com/lestrrat-go/jwx/v2 v2.0.16/go.mod h1:jBHyESp4e7QxfERM0UKkQ80/94paqNIEcdEfiUYz5zE=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA=
github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/quicktemplate v1.7.0 h1:LUPTJmlVcb46OOUY3IeD9DojFpAVbsG+5WFTcjMJzCM=
github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
go4.org/intern v0.0.0-20230205224052-192e9f60865c h1:b8WZ7Ja8nKegYxfwDLLwT00ZKv4lXAQrw8LYPK+cHSI=
go4.org/intern v0.0.0-20230205224052-192e9f60865c/go.mod h1:RJ0SVrOMpxLhgb5noIV+09zI1RsRlMsbUcSxpWHqbrE=
go4.org/intern v0.0.0-20230525184215-6c62f75575cb h1:ae7kzL5Cfdmcecbh22ll7lYP3iuUdnfnhiPcSaDgH/8=
go4.org/intern v0.0.0-20230525184215-6c62f75575cb/go.mod h1:Ycrt6raEcnF5FTsLiLKkhBTO6DPX3RCUCUVnks3gFJU=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230204201903-c31fa085b70e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296 h1:QJ/xcIANMLApehfgPCHnfK1hZiaMmbaTVmPv7DAoTbo=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230221090011-e4bae7ad2296/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo=
golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -126,56 +127,74 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc=
golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
inet.af/netaddr v0.0.0-20220811202034-502d2d690317 h1:U2fwK6P2EqmopP/hFLTOAjWTki0qgd4GMJn5X8wOleU=
inet.af/netaddr v0.0.0-20220811202034-502d2d690317/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM=
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo=
lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q=
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0=
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
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/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs=
modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.21.0 h1:4aP4MdUf15i3R3M2mx6Q90WHKz3nZLoz96zlB6tNdow=
modernc.org/sqlite v1.21.0/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws=
modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8=
modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
source.toby3d.me/toby3d/form v0.3.0 h1:kI8apdFeVr+koqTTGVoIRiR5NMqjrhCJlajYlu+1bVw=
source.toby3d.me/toby3d/form v0.3.0/go.mod h1:drlHMC+j/gb5zsttCSwx8qcYsbaRW+wFfE8bK6y+oeY=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
source.toby3d.me/toby3d/form v0.4.0 h1:p4erlFQZpWi64oHQVYsNe8FKT75ZwnExELk69ZDoQO8=
source.toby3d.me/toby3d/form v0.4.0/go.mod h1:drlHMC+j/gb5zsttCSwx8qcYsbaRW+wFfE8bK6y+oeY=
willnorris.com/go/microformats v1.2.0 h1:73pzJCLJM69kYE5qsLI9OOC/7sImNVOzya9EQ0+1wmM=
willnorris.com/go/microformats v1.2.0/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4=

View file

@ -23,16 +23,16 @@ type (
NewHandlerOptions struct {
Auth auth.UseCase
Clients client.UseCase
Config domain.Config
Matcher language.Matcher
Profiles profile.UseCase
Config domain.Config
}
Handler struct {
clients client.UseCase
config domain.Config
matcher language.Matcher
useCase auth.UseCase
config domain.Config
}
)
@ -45,7 +45,7 @@ func NewHandler(opts NewHandlerOptions) *Handler {
}
}
func (h *Handler) Handler() http.Handler {
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
chain := middleware.Chain{
middleware.CSRFWithConfig(middleware.CSRFConfig{
Skipper: func(_ http.ResponseWriter, r *http.Request) bool {
@ -68,8 +68,7 @@ func (h *Handler) Handler() http.Handler {
Skipper: func(_ http.ResponseWriter, r *http.Request) bool {
head, _ := urlutil.ShiftPath(r.URL.Path)
return r.Method != http.MethodPost ||
head != "verify" ||
return r.Method != http.MethodPost || head != "verify" ||
r.PostFormValue("authorize") == "deny"
},
Validator: func(_ http.ResponseWriter, _ *http.Request, login, password string) (bool, error) {
@ -84,31 +83,29 @@ func (h *Handler) Handler() http.Handler {
}),
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
head, _ := urlutil.ShiftPath(r.URL.Path)
head, _ := urlutil.ShiftPath(r.URL.Path)
switch r.Method {
default:
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
case http.MethodGet, "":
if head != "" {
http.NotFound(w, r)
switch r.Method {
default:
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
case http.MethodGet, "":
if head != "" {
http.NotFound(w, r)
return
}
chain.Handler(h.handleAuthorize).ServeHTTP(w, r)
case http.MethodPost:
switch head {
default:
http.NotFound(w, r)
case "":
chain.Handler(h.handleExchange).ServeHTTP(w, r)
case "verify":
chain.Handler(h.handleVerify).ServeHTTP(w, r)
}
return
}
})
chain.Handler(h.handleAuthorize).ServeHTTP(w, r)
case http.MethodPost:
switch head {
default:
http.NotFound(w, r)
case "":
chain.Handler(h.handleExchange).ServeHTTP(w, r)
case "verify":
chain.Handler(h.handleVerify).ServeHTTP(w, r)
}
}
}
func (h *Handler) handleAuthorize(w http.ResponseWriter, r *http.Request) {
@ -169,7 +166,7 @@ func (h *Handler) handleAuthorize(w http.ResponseWriter, r *http.Request) {
Client: client,
Me: &req.Me,
RedirectURI: &req.RedirectURI,
CodeChallengeMethod: *req.CodeChallengeMethod,
CodeChallengeMethod: req.CodeChallengeMethod,
ResponseType: req.ResponseType,
CodeChallenge: req.CodeChallenge,
State: req.State,
@ -210,7 +207,7 @@ func (h *Handler) handleVerify(w http.ResponseWriter, r *http.Request) {
ClientID: req.ClientID,
Me: req.Me,
RedirectURI: req.RedirectURI.URL,
CodeChallengeMethod: *req.CodeChallengeMethod,
CodeChallengeMethod: req.CodeChallengeMethod,
Scope: req.Scope,
CodeChallenge: req.CodeChallenge,
})
@ -271,18 +268,8 @@ func (h *Handler) handleExchange(w http.ResponseWriter, r *http.Request) {
return
}
var userInfo *AuthProfileResponse
if profile != nil {
userInfo = &AuthProfileResponse{
Email: profile.GetEmail(),
Photo: &domain.URL{URL: profile.GetPhoto()},
URL: &domain.URL{URL: profile.GetURL()},
Name: profile.GetName(),
}
}
_ = encoder.Encode(&AuthExchangeResponse{
Me: *me,
Profile: userInfo,
Me: me.String(),
Profile: NewAuthProfileResponse(profile),
})
}

View file

@ -22,7 +22,7 @@ type (
Me domain.Me `form:"me"`
// The hashing method used to calculate the code challenge.
CodeChallengeMethod *domain.CodeChallengeMethod `form:"code_challenge_method,omitempty"`
CodeChallengeMethod domain.CodeChallengeMethod `form:"code_challenge_method,omitempty"`
// Indicates to the authorization server that an authorization
// code should be returned as the response.
@ -46,16 +46,16 @@ type (
}
AuthVerifyRequest struct {
ClientID domain.ClientID `form:"client_id"`
Me domain.Me `form:"me"`
RedirectURI domain.URL `form:"redirect_uri"`
CodeChallengeMethod *domain.CodeChallengeMethod `form:"code_challenge_method,omitempty"`
ResponseType domain.ResponseType `form:"response_type"`
Authorize string `form:"authorize"`
CodeChallenge string `form:"code_challenge,omitempty"`
State string `form:"state"`
Provider string `form:"provider"`
Scope domain.Scopes `form:"scope[],omitempty"`
ClientID domain.ClientID `form:"client_id"`
Me domain.Me `form:"me"`
RedirectURI domain.URL `form:"redirect_uri"`
CodeChallengeMethod domain.CodeChallengeMethod `form:"code_challenge_method,omitempty"`
ResponseType domain.ResponseType `form:"response_type"`
Authorize string `form:"authorize"`
CodeChallenge string `form:"code_challenge,omitempty"`
State string `form:"state"`
Provider string `form:"provider"`
Scope domain.Scopes `form:"scope[],omitempty"`
}
AuthExchangeRequest struct {
@ -79,15 +79,15 @@ type (
}
AuthExchangeResponse struct {
Me domain.Me `json:"me"`
Profile *AuthProfileResponse `json:"profile,omitempty"`
Me string `json:"me"`
}
AuthProfileResponse struct {
Email *domain.Email `json:"email,omitempty"`
Photo *domain.URL `json:"photo,omitempty"`
URL *domain.URL `json:"url,omitempty"`
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
Photo string `json:"photo,omitempty"`
URL string `json:"url,omitempty"`
Name string `json:"name,omitempty"`
}
)
@ -95,7 +95,7 @@ func NewAuthAuthorizationRequest() *AuthAuthorizationRequest {
return &AuthAuthorizationRequest{
ClientID: domain.ClientID{},
CodeChallenge: "",
CodeChallengeMethod: &domain.CodeChallengeMethodUnd,
CodeChallengeMethod: domain.CodeChallengeMethodUnd,
Me: domain.Me{},
RedirectURI: domain.URL{},
ResponseType: domain.ResponseTypeUnd,
@ -129,7 +129,7 @@ func NewAuthVerifyRequest() *AuthVerifyRequest {
Authorize: "",
ClientID: domain.ClientID{},
CodeChallenge: "",
CodeChallengeMethod: &domain.CodeChallengeMethodUnd,
CodeChallengeMethod: domain.CodeChallengeMethodUnd,
Me: domain.Me{},
Provider: "",
RedirectURI: domain.URL{},
@ -139,7 +139,7 @@ func NewAuthVerifyRequest() *AuthVerifyRequest {
}
}
//nolint:funlen,cyclop
//nolint:cyclop
func (r *AuthVerifyRequest) bind(req *http.Request) error {
indieAuthError := new(domain.Error)
@ -192,3 +192,27 @@ func (r *AuthExchangeRequest) bind(req *http.Request) error {
return nil
}
func NewAuthProfileResponse(in *domain.Profile) *AuthProfileResponse {
out := new(AuthProfileResponse)
if in == nil {
return out
}
out.Name = in.Name
if in.URL != nil {
out.URL = in.URL.String()
}
if in.Email != nil {
out.Email = in.Email.String()
}
if in.Photo != nil {
out.Photo = in.Photo.String()
}
return out
}

View file

@ -31,13 +31,14 @@ type Dependencies struct {
authService auth.UseCase
clients client.Repository
clientService client.UseCase
config *domain.Config
matcher language.Matcher
profiles profile.Repository
sessions session.Repository
users user.Repository
config *domain.Config
}
//nolint:funlen
func TestAuthorize(t *testing.T) {
t.Parallel()
@ -85,7 +86,7 @@ func TestAuthorize(t *testing.T) {
Clients: deps.clientService,
Config: *deps.config,
Matcher: deps.matcher,
}).Handler().ServeHTTP(w, req)
}).ServeHTTP(w, req)
resp := w.Result()
@ -98,7 +99,7 @@ func TestAuthorize(t *testing.T) {
t.Errorf("%s %s = %d, want %d", req.Method, u.String(), resp.StatusCode, http.StatusOK)
}
const expResult = `Authorize application`
expResult := `Authorize ` + client.Name
if result := string(body); !strings.Contains(result, expResult) {
t.Errorf("%s %s = %s, want %s", req.Method, u.String(), result, expResult)
}
@ -113,7 +114,7 @@ func NewDependencies(tb testing.TB) Dependencies {
users := userrepo.NewMemoryUserRepository()
sessions := sessionrepo.NewMemorySessionRepository(*config)
profiles := profilerepo.NewMemoryProfileRepository()
authService := ucase.NewAuthUseCase(sessions, profiles, config)
authService := ucase.NewAuthUseCase(sessions, profiles, *config)
clientService := clientucase.NewClientUseCase(clients)
return Dependencies{

View file

@ -13,8 +13,8 @@ type (
Me domain.Me
RedirectURI *url.URL
CodeChallengeMethod domain.CodeChallengeMethod
Scope domain.Scopes
CodeChallenge string
Scope domain.Scopes
}
ExchangeOptions struct {

View file

@ -12,13 +12,13 @@ import (
)
type authUseCase struct {
config *domain.Config
sessions session.Repository
profiles profile.Repository
config domain.Config
}
// NewAuthUseCase creates a new authentication use case.
func NewAuthUseCase(sessions session.Repository, profiles profile.Repository, config *domain.Config) auth.UseCase {
func NewAuthUseCase(sessions session.Repository, profiles profile.Repository, config domain.Config) auth.UseCase {
return &authUseCase{
config: config,
sessions: sessions,

View file

@ -39,26 +39,24 @@ func NewHandler(opts NewHandlerOptions) *Handler {
}
}
func (h *Handler) Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "" && r.Method != http.MethodGet {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != "" && r.Method != http.MethodGet {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
return
}
var head string
head, r.URL.Path = urlutil.ShiftPath(r.URL.Path)
var head string
head, r.URL.Path = urlutil.ShiftPath(r.URL.Path)
switch head {
default:
http.NotFound(w, r)
case "":
h.handleRender(w, r)
case "callback":
h.handleCallback(w, r)
}
})
switch head {
default:
http.NotFound(w, r)
case "":
h.handleRender(w, r)
case "callback":
h.handleCallback(w, r)
}
}
func (h *Handler) handleRender(w http.ResponseWriter, r *http.Request) {
@ -93,7 +91,7 @@ func (h *Handler) handleRender(w http.ResponseWriter, r *http.Request) {
})
}
//nolint:unlen
//nolint:funlen
func (h *Handler) handleCallback(w http.ResponseWriter, r *http.Request) {
if r.Method != "" && r.Method != http.MethodGet {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)

View file

@ -10,10 +10,10 @@ import (
type ClientCallbackRequest struct {
Error domain.ErrorCode `form:"error,omitempty"`
Iss domain.ClientID `form:"iss"`
Code string `form:"code"`
Iss domain.ClientID `form:"iss,omitempty"`
Code string `form:"code,omitempty"`
ErrorDescription string `form:"error_description,omitempty"`
State string `form:"state"`
State string `form:"state,omitempty"`
}
func (req *ClientCallbackRequest) bind(r *http.Request) error {

View file

@ -41,7 +41,7 @@ func TestRead(t *testing.T) {
Config: *deps.config,
Matcher: deps.matcher,
Tokens: deps.tokenService,
}).Handler().ServeHTTP(w, req)
}).ServeHTTP(w, req)
if resp := w.Result(); resp.StatusCode != http.StatusOK {
t.Errorf("%s %s = %d, want %d", req.Method, req.RequestURI, resp.StatusCode, http.StatusOK)
@ -58,7 +58,7 @@ func NewDependencies(tb testing.TB) Dependencies {
tokens := tokenrepo.NewMemoryTokenRepository()
profiles := profilerepo.NewMemoryProfileRepository()
tokenService := tokenucase.NewTokenUseCase(tokenucase.Config{
Config: config,
Config: *config,
Profiles: profiles,
Sessions: sessions,
Tokens: tokens,

View file

@ -8,27 +8,40 @@ import (
"net/http"
"net/url"
"github.com/tomnomnom/linkheader"
"golang.org/x/exp/slices"
"willnorris.com/go/microformats"
"source.toby3d.me/toby3d/auth/internal/client"
"source.toby3d.me/toby3d/auth/internal/common"
"source.toby3d.me/toby3d/auth/internal/domain"
"source.toby3d.me/toby3d/auth/internal/httputil"
)
type httpClientRepository struct {
client *http.Client
}
type (
//nolint:tagliatelle,lll
Response struct {
TicketEndpoint domain.URL `json:"ticket_endpoint"`
AuthorizationEndpoint domain.URL `json:"authorization_endpoint"`
IntrospectionEndpoint domain.URL `json:"introspection_endpoint"`
RevocationEndpoint domain.URL `json:"revocation_endpoint,omitempty"`
ServiceDocumentation domain.URL `json:"service_documentation,omitempty"`
TokenEndpoint domain.URL `json:"token_endpoint"`
UserinfoEndpoint domain.URL `json:"userinfo_endpoint,omitempty"`
Microsub domain.URL `json:"microsub"`
Issuer domain.URL `json:"issuer"`
Micropub domain.URL `json:"micropub"`
GrantTypesSupported []domain.GrantType `json:"grant_types_supported,omitempty"`
IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`
ScopesSupported []domain.Scope `json:"scopes_supported,omitempty"`
ResponseTypesSupported []domain.ResponseType `json:"response_types_supported,omitempty"`
CodeChallengeMethodsSupported []domain.CodeChallengeMethod `json:"code_challenge_methods_supported"`
AuthorizationResponseIssParameterSupported bool `json:"authorization_response_iss_parameter_supported,omitempty"`
}
const (
DefaultMaxRedirectsCount int = 10
hApp string = "h-app"
hXApp string = "h-x-app"
propertyLogo string = "logo"
propertyName string = "name"
propertyURL string = "url"
relRedirectURI string = "redirect_uri"
httpClientRepository struct {
client *http.Client
}
)
func NewHTTPClientRepository(c *http.Client) client.Repository {
@ -43,6 +56,18 @@ func (httpClientRepository) Create(_ context.Context, _ domain.Client) error {
}
func (repo httpClientRepository) Get(ctx context.Context, cid domain.ClientID) (*domain.Client, error) {
out := &domain.Client{
ID: cid,
RedirectURI: make([]*url.URL, 0),
Logo: nil,
URL: nil,
Name: "",
}
if cid.IsLocalhost() {
return out, nil
}
resp, err := repo.client.Get(cid.String())
if err != nil {
return nil, fmt.Errorf("failed to make a request to the client: %w", err)
@ -52,77 +77,82 @@ func (repo httpClientRepository) Get(ctx context.Context, cid domain.ClientID) (
return nil, fmt.Errorf("%w: status on client page is not 200", client.ErrNotExist)
}
client := &domain.Client{
ID: cid,
RedirectURI: make([]*url.URL, 0),
Logo: make([]*url.URL, 0),
URL: make([]*url.URL, 0),
Name: make([]string, 0),
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("cannot read response body: %w", err)
}
extract(resp.Body, resp.Request.URL, client, resp.Header.Get(common.HeaderLink))
// NOTE(toby3d): fetch redirect uri's and application profile from HTML nodes
mf2 := microformats.Parse(bytes.NewReader(body), resp.Request.URL)
return client, nil
}
//nolint:gocognit,cyclop
func extract(r io.Reader, u *url.URL, dst *domain.Client, header string) {
body, _ := io.ReadAll(r)
for _, endpoint := range httputil.ExtractEndpoints(bytes.NewReader(body), u, header, relRedirectURI) {
if !containsUrl(dst.RedirectURI, endpoint) {
dst.RedirectURI = append(dst.RedirectURI, endpoint)
}
}
for _, itemType := range []string{hApp, hXApp} {
for _, name := range httputil.ExtractProperty(bytes.NewReader(body), u, itemType, propertyName) {
if n, ok := name.(string); ok && !slices.Contains(dst.Name, n) {
dst.Name = append(dst.Name, n)
}
}
for _, logo := range httputil.ExtractProperty(bytes.NewReader(body), u, itemType, propertyLogo) {
var logoURL *url.URL
var err error
switch l := logo.(type) {
case string:
logoURL, err = url.Parse(l)
case map[string]string:
if value, ok := l["value"]; ok {
logoURL, err = url.Parse(value)
}
}
if err != nil || containsUrl(dst.Logo, logoURL) {
continue
}
dst.Logo = append(dst.Logo, logoURL)
}
for _, property := range httputil.ExtractProperty(bytes.NewReader(body), u, itemType, propertyURL) {
prop, ok := property.(string)
if !ok {
continue
}
if u, err := url.Parse(prop); err == nil && !containsUrl(dst.URL, u) {
dst.URL = append(dst.URL, u)
}
}
}
}
func containsUrl(src []*url.URL, find *url.URL) bool {
for i := range src {
if src[i].String() != find.String() {
for i := range mf2.Items {
if !slices.Contains(mf2.Items[i].Type, common.HApp) &&
!slices.Contains(mf2.Items[i].Type, common.HXApp) {
continue
}
return true
parseProfile(mf2.Items[i].Properties, out)
}
return false
for _, val := range mf2.Rels[common.RelRedirectURI] {
var u *url.URL
if u, err = url.Parse(val); err == nil {
out.RedirectURI = append(out.RedirectURI, u)
}
}
// NOTE(toby3d): fetch redirect uri's from Link header
for _, link := range linkheader.Parse(resp.Header.Get(common.HeaderLink)) {
if link.Rel != common.RelRedirectURI {
continue
}
var u *url.URL
if u, err = url.Parse(link.URL); err == nil {
out.RedirectURI = append(out.RedirectURI, u)
}
}
return out, nil
}
func parseProfile(src map[string][]any, dst *domain.Client) {
for _, val := range src[common.PropertyName] {
v, ok := val.(string)
if !ok {
continue
}
dst.Name = v
break
}
for _, val := range src[common.PropertyURL] {
v, ok := val.(string)
if !ok {
continue
}
var err error
if dst.URL, err = url.Parse(v); err != nil {
continue
}
break
}
for _, val := range src[common.PropertyLogo] {
v, ok := val.(string)
if !ok {
continue
}
var err error
if dst.Logo, err = url.Parse(v); err != nil {
continue
}
break
}
}

View file

@ -73,9 +73,9 @@ func TestGet(t *testing.T) {
func testHandler(tb testing.TB, client domain.Client) http.Handler {
tb.Helper()
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set(common.HeaderContentType, common.MIMETextHTMLCharsetUTF8)
w.Header().Set(common.HeaderLink, `<`+client.RedirectURI[0].String()+`>; rel="redirect_uri"`)
fmt.Fprintf(w, testBody, client.Name[0], client.URL[0], client.Logo[0], client.RedirectURI[1])
w.Header().Set(common.HeaderLink, `<`+client.RedirectURI[1].String()+`>; rel="redirect_uri"`)
fmt.Fprintf(w, testBody, client.Name, client.URL, client.Logo, client.RedirectURI[0])
})
}

View file

@ -22,10 +22,10 @@ func TestDiscovery(t *testing.T) {
}
for _, tc := range []struct {
name string
expError error
in *domain.Client
out *domain.Client
expError error
name string
}{{
name: "default",
in: testClient,

View file

@ -1,5 +1,7 @@
package common
const charsetUTF8 = "charset=UTF-8"
const (
MIMEApplicationForm string = "application/x-www-form-urlencoded"
MIMEApplicationJSON string = "application/json"
@ -8,8 +10,6 @@ const (
MIMETextHTMLCharsetUTF8 string = MIMETextHTML + "; " + charsetUTF8
MIMETextPlain string = "text/plain"
MIMETextPlainCharsetUTF8 string = MIMETextPlain + "; " + charsetUTF8
charsetUTF8 = "charset=UTF-8"
)
const (
@ -27,4 +27,29 @@ const (
HeaderXCSRFToken string = "X-CSRF-Token"
)
const (
HApp string = "h-app"
HCard string = "h-card"
HXApp string = "h-x-app"
)
const (
PropertyEmail string = "email"
PropertyLogo string = "logo"
PropertyName string = "name"
PropertyPhoto string = "photo"
PropertyURL string = "url"
)
const (
RelAuthn string = "authn"
RelAuthorizationEndpoint string = "authorization_endpoint"
RelIndieAuthMetadata string = "indieauth-metadata"
RelMicropub string = "micropub"
RelMicrosub string = "microsub"
RelRedirectURI string = "redirect_uri"
RelTicketEndpoint string = "ticket_endpoint"
RelTokenEndpoint string = "token_endpoint"
)
const Und string = "und"

View file

@ -1,36 +0,0 @@
package domain
import "net/url"
type App struct {
Logo []*url.URL
URL []*url.URL
Name []string
}
// GetName safe returns first name, if any.
func (a App) GetName() string {
if len(a.Name) == 0 {
return ""
}
return a.Name[0]
}
// GetURL safe returns first URL, if any.
func (a App) GetURL() *url.URL {
if len(a.URL) == 0 {
return nil
}
return a.URL[0]
}
// GetLogo safe returns first logo, if any.
func (a App) GetLogo() *url.URL {
if len(a.Logo) == 0 {
return nil
}
return a.Logo[0]