this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

base

Hailey 3f5828ba

+546
+40
Makefile
··· 1 + SHELL = /bin/bash 2 + .SHELLFLAGS = -o pipefail -c 3 + 4 + .PHONY: help 5 + help: ## Print info about all commands 6 + @echo "Commands:" 7 + @echo 8 + @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[01;32m%-20s\033[0m %s\n", $$1, $$2}' 9 + 10 + .PHONY: build 11 + build: ## Build all executables 12 + go build -o oauth . 13 + 14 + .PHONY: all 15 + all: build 16 + 17 + .PHONY: test 18 + test: ## Run tests 19 + go test ./... 20 + 21 + .PHONY: coverage-html 22 + coverage-html: ## Generate test coverage report and open in browser 23 + go test ./... -coverpkg=./... -coverprofile=test-coverage.out 24 + go tool cover -html=test-coverage.out 25 + 26 + .PHONY: lint 27 + lint: ## Verify code style and run static checks 28 + go vet ./... 29 + test -z $(gofmt -l ./...) 30 + 31 + .PHONY: fmt 32 + fmt: ## Run syntax re-formatting (modify in place) 33 + go fmt ./... 34 + 35 + .PHONY: check 36 + check: ## Compile everything, checking syntax (does not output binaries) 37 + go build ./... 38 + 39 + .env: 40 + if [ ! -f ".env" ]; then cp example.dev.env .env; fi
+39
go.mod
··· 1 + module github.com/haileyok/atproto-oauth-golang 2 + 3 + go 1.24.0 4 + 5 + require ( 6 + github.com/lestrrat-go/jwx/v2 v2.0.12 7 + github.com/stretchr/testify v1.10.0 8 + ) 9 + 10 + require ( 11 + github.com/bluesky-social/indigo v0.0.0-20250222003125-2503553ea604 // indirect 12 + github.com/davecgh/go-spew v1.1.1 // indirect 13 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 14 + github.com/goccy/go-json v0.10.2 // indirect 15 + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect 16 + github.com/gorilla/context v1.1.2 // indirect 17 + github.com/gorilla/securecookie v1.1.2 // indirect 18 + github.com/gorilla/sessions v1.4.0 // indirect 19 + github.com/labstack/echo-contrib v0.17.2 // indirect 20 + github.com/labstack/echo/v4 v4.13.3 // indirect 21 + github.com/labstack/gommon v0.4.2 // indirect 22 + github.com/lestrrat-go/blackmagic v1.0.1 // indirect 23 + github.com/lestrrat-go/httpcc v1.0.1 // indirect 24 + github.com/lestrrat-go/httprc v1.0.4 // indirect 25 + github.com/lestrrat-go/iter v1.0.2 // indirect 26 + github.com/lestrrat-go/option v1.0.1 // indirect 27 + github.com/mattn/go-colorable v0.1.13 // indirect 28 + github.com/mattn/go-isatty v0.0.20 // indirect 29 + github.com/pmezard/go-difflib v1.0.0 // indirect 30 + github.com/segmentio/asm v1.2.0 // indirect 31 + github.com/valyala/bytebufferpool v1.0.0 // indirect 32 + github.com/valyala/fasttemplate v1.2.2 // indirect 33 + golang.org/x/crypto v0.31.0 // indirect 34 + golang.org/x/net v0.33.0 // indirect 35 + golang.org/x/sys v0.28.0 // indirect 36 + golang.org/x/text v0.21.0 // indirect 37 + golang.org/x/time v0.8.0 // indirect 38 + gopkg.in/yaml.v3 v3.0.1 // indirect 39 + )
+115
go.sum
··· 1 + github.com/bluesky-social/indigo v0.0.0-20250222003125-2503553ea604 h1:rceaPCufVEkobTmyISJhvY4kzaPKtSujN427CGWpvHw= 2 + github.com/bluesky-social/indigo v0.0.0-20250222003125-2503553ea604/go.mod h1:NVBwZvbBSa93kfyweAmKwOLYawdVHdwZ9s+GZtBBVLA= 3 + github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 + github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 7 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 8 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 9 + github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 10 + github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 11 + github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= 12 + github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= 13 + github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 14 + github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= 15 + github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= 16 + github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= 17 + github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= 18 + github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= 19 + github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= 20 + github.com/labstack/echo-contrib v0.17.2 h1:K1zivqmtcC70X9VdBFdLomjPDEVHlrcAObqmuFj1c6w= 21 + github.com/labstack/echo-contrib v0.17.2/go.mod h1:NeDh3PX7j/u+jR4iuDt1zHmWZSCz9c/p9mxXcDpyS8E= 22 + github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= 23 + github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= 24 + github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= 25 + github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= 26 + github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= 27 + github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= 28 + github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= 29 + github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= 30 + github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= 31 + github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= 32 + github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= 33 + github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= 34 + github.com/lestrrat-go/jwx/v2 v2.0.12 h1:3d589+5w/b9b7S3DneICPW16AqTyYXB7VRjgluSDWeA= 35 + github.com/lestrrat-go/jwx/v2 v2.0.12/go.mod h1:Mq4KN1mM7bp+5z/W5HS8aCNs5RKZ911G/0y2qUjAQuQ= 36 + github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 37 + github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= 38 + github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 39 + github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 40 + github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 41 + github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 42 + github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 43 + github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 44 + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 45 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 46 + github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= 47 + github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= 48 + github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 49 + github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 50 + github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 51 + github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 52 + github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 53 + github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 54 + github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 55 + github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 56 + github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 57 + github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 58 + github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 59 + github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= 60 + github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= 61 + github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 62 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 63 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 64 + golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 65 + golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 66 + golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 67 + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 68 + golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 69 + golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 70 + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 71 + golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 72 + golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 73 + golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 74 + golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 75 + golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 76 + golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 77 + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 78 + golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 79 + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 80 + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 81 + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 82 + golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 83 + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 84 + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 85 + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 86 + golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 87 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 88 + golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 89 + golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 90 + golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 91 + golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 92 + golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 93 + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 94 + golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 95 + golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 96 + golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 97 + golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 98 + golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 99 + golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 100 + golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 101 + golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 102 + golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 103 + golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 104 + golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 105 + golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= 106 + golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 107 + golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 108 + golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 109 + golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 110 + golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 111 + golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 112 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 113 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 114 + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 115 + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+11
helpers.go
··· 1 + package oauth 2 + 3 + func tokenInSet(tok string, set []string) bool { 4 + for _, setTok := range set { 5 + if tok == setTok { 6 + return true 7 + } 8 + } 9 + 10 + return false 11 + }
+128
oauth.go
··· 1 + package oauth 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "io" 7 + "net/http" 8 + "net/url" 9 + "time" 10 + ) 11 + 12 + type OauthClient struct { 13 + h *http.Client 14 + } 15 + 16 + type OauthClientArgs struct { 17 + h *http.Client 18 + } 19 + 20 + func NewOauthClient(args OauthClientArgs) *OauthClient { 21 + if args.h == nil { 22 + args.h = &http.Client{ 23 + Timeout: 5 * time.Second, 24 + } 25 + } 26 + return &OauthClient{ 27 + h: args.h, 28 + } 29 + } 30 + 31 + func (o *OauthClient) ResolvePDSAuthServer(ctx context.Context, ustr string) (string, error) { 32 + u, err := isSafeAndParsed(ustr) 33 + if err != nil { 34 + return "", err 35 + } 36 + 37 + u.Path = "/.well-known/oauth-protected-resource" 38 + 39 + req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil) 40 + if err != nil { 41 + return "", fmt.Errorf("error creating request for oauth protected resource: %w", err) 42 + } 43 + 44 + resp, err := o.h.Do(req) 45 + if err != nil { 46 + return "", fmt.Errorf("could not get response from server: %w", err) 47 + } 48 + defer resp.Body.Close() 49 + 50 + if resp.StatusCode != http.StatusOK { 51 + io.Copy(io.Discard, resp.Body) 52 + return "", fmt.Errorf("received non-200 response from pds. code was %d", resp.StatusCode) 53 + } 54 + 55 + b, err := io.ReadAll(resp.Body) 56 + if err != nil { 57 + return "", fmt.Errorf("could not read body: %w", err) 58 + } 59 + 60 + var resource OauthProtectedResource 61 + if err := resource.UnmarshalJSON(b); err != nil { 62 + return "", fmt.Errorf("could not unmarshal json: %w", err) 63 + } 64 + 65 + if len(resource.AuthorizationServers) == 0 { 66 + return "", fmt.Errorf("oauth protected resource contained no authorization servers") 67 + } 68 + 69 + return resource.AuthorizationServers[0], nil 70 + } 71 + 72 + func (o *OauthClient) FetchAuthServerMetadata(ctx context.Context, ustr string) (any, error) { 73 + u, err := isSafeAndParsed(ustr) 74 + if err != nil { 75 + return nil, err 76 + } 77 + 78 + u.Path = "/.well-known/oauth-authorization-server" 79 + 80 + req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil) 81 + if err != nil { 82 + return nil, fmt.Errorf("error creating request to fetch auth metadata: %w", err) 83 + } 84 + 85 + resp, err := o.h.Do(req) 86 + if err != nil { 87 + return nil, fmt.Errorf("error getting response for auth metadata: %w", err) 88 + } 89 + defer resp.Body.Close() 90 + 91 + if resp.StatusCode != http.StatusOK { 92 + io.Copy(io.Discard, resp.Body) 93 + return nil, fmt.Errorf("received non-200 response from pds. status code was %d", resp.StatusCode) 94 + } 95 + 96 + b, err := io.ReadAll(resp.Body) 97 + if err != nil { 98 + return nil, fmt.Errorf("could not read body for metadata response: %w", err) 99 + } 100 + 101 + var metadata OauthAuthorizationMetadata 102 + if err := metadata.UnmarshalJSON(b); err != nil { 103 + return nil, fmt.Errorf("could not unmarshal metadata: %w", err) 104 + } 105 + 106 + if err := metadata.Validate(u); err != nil { 107 + return nil, fmt.Errorf("could not validate metadata: %w", err) 108 + } 109 + 110 + return metadata, nil 111 + } 112 + 113 + // func ClientAssertionJwt(clientId, authServerUrl string, clientSecretJwk jwk.Key) { 114 + // clientAssertion := jwt.NewBuilder().Issuer(clientId).Subject(clientId).Audience(authServerUrl).IssuedAt(time.Now().Add() 115 + // } 116 + 117 + func isSafeAndParsed(ustr string) (*url.URL, error) { 118 + u, err := url.Parse(ustr) 119 + if err != nil { 120 + return nil, err 121 + } 122 + 123 + if u.Scheme != "https" { 124 + return nil, fmt.Errorf("input url is not https") 125 + } 126 + 127 + return u, nil 128 + }
+32
oauth_test.go
··· 1 + package oauth 2 + 3 + import ( 4 + "context" 5 + "testing" 6 + 7 + "github.com/stretchr/testify/assert" 8 + ) 9 + 10 + var ( 11 + ctx = context.Background() 12 + oauthClient = NewOauthClient(OauthClientArgs{}) 13 + ) 14 + 15 + func TestResolvePDSAuthServer(t *testing.T) { 16 + assert := assert.New(t) 17 + 18 + authServer, err := oauthClient.ResolvePDSAuthServer(ctx, "https://pds.haileyok.com") 19 + 20 + assert.NoError(err) 21 + assert.NotEmpty(authServer) 22 + assert.Equal("https://pds.haileyok.com", authServer) 23 + } 24 + 25 + func TestFetchAuthServerMetadata(t *testing.T) { 26 + assert := assert.New(t) 27 + 28 + meta, err := oauthClient.FetchAuthServerMetadata(ctx, "https://pds.haileyok.com") 29 + 30 + assert.NoError(err) 31 + assert.IsType(OauthAuthorizationMetadata{}, meta) 32 + }
+181
types.go
··· 1 + package oauth 2 + 3 + import ( 4 + "encoding/json" 5 + "fmt" 6 + "net/url" 7 + ) 8 + 9 + type OauthProtectedResource struct { 10 + Resource string `json:"resource"` 11 + AuthorizationServers []string `json:"authorization_servers"` 12 + ScopesSupported []string `json:"scopes_supported"` 13 + BearerMethodsSupported []string `json:"bearer_methods_supported"` 14 + ResourceDocumentation string `json:"resource_documentation"` 15 + } 16 + 17 + func (opr *OauthProtectedResource) UnmarshalJSON(b []byte) error { 18 + type Tmp OauthProtectedResource 19 + var tmp Tmp 20 + 21 + if err := json.Unmarshal(b, &tmp); err != nil { 22 + return err 23 + } 24 + 25 + *opr = OauthProtectedResource(tmp) 26 + 27 + return nil 28 + } 29 + 30 + type OauthAuthorizationMetadata struct { 31 + Issuer string `json:"issuer"` 32 + RequestParameterSupported bool `json:"request_parameter_supported"` 33 + RequestUriParameterSupported bool `json:"request_uri_parameter_supported"` 34 + RequireRequestUriRegistration *bool `json:"require_request_uri_registration,omitempty"` 35 + ScopesSupported []string `json:"scopes_supported"` 36 + SubjectTypesSupported []string `json:"subject_types_supported"` 37 + ResponseTypesSupported []string `json:"response_types_supported"` 38 + ResponseModesSupported []string `json:"response_modes_supported"` 39 + GrantTypesSupported []string `json:"grant_types_supported"` 40 + CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"` 41 + UILocalesSupported []string `json:"ui_locales_supported"` 42 + DisplayValuesSupported []string `json:"display_values_supported"` 43 + RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"` 44 + AuthorizationResponseISSParameterSupported bool `json:"authorization_response_iss_parameter_supported"` 45 + RequestObjectEncryptionAlgValuesSupported []string `json:"request_object_encryption_alg_values_supported"` 46 + RequestObjectEncryptionEncValuesSupported []string `json:"request_object_encryption_enc_values_supported"` 47 + JwksUri string `json:"jwks_uri"` 48 + AuthorizationEndpoint string `json:"authorization_endpoint"` 49 + TokenEndpoint string `json:"token_endpoint"` 50 + TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"` 51 + TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported"` 52 + RevocationEndpoint string `json:"revocation_endpoint"` 53 + IntrospectionEndpoint string `json:"introspection_endpoint"` 54 + PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint"` 55 + RequirePushedAuthorizationRequests bool `json:"require_pushed_authorization_requests"` 56 + DpopSigningAlgValuesSupported []string `json:"dpop_signing_alg_values_supported"` 57 + ProtectedResources []string `json:"protected_resources"` 58 + ClientIDMetadataDocumentSupported bool `json:"client_id_metadata_document_supported"` 59 + } 60 + 61 + func (oam *OauthAuthorizationMetadata) Validate(fetch_url *url.URL) error { 62 + if fetch_url == nil { 63 + return fmt.Errorf("fetch_url was nil") 64 + } 65 + 66 + iu, err := url.Parse(oam.Issuer) 67 + if err != nil { 68 + oam = nil 69 + return err 70 + } 71 + 72 + if iu.Hostname() != fetch_url.Hostname() { 73 + oam = nil 74 + return fmt.Errorf("issuer hostname does not match fetch url hostname") 75 + } 76 + 77 + if iu.Scheme != "https" { 78 + oam = nil 79 + return fmt.Errorf("issuer url is not https") 80 + } 81 + 82 + if iu.Port() != "" { 83 + oam = nil 84 + return fmt.Errorf("issuer port is not empty") 85 + } 86 + 87 + if iu.Path != "" && iu.Path != "/" { 88 + oam = nil 89 + return fmt.Errorf("issuer path is not /") 90 + } 91 + 92 + if iu.RawQuery != "" { 93 + oam = nil 94 + return fmt.Errorf("issuer url params are not empty") 95 + } 96 + 97 + if !tokenInSet("code", oam.ResponseTypesSupported) { 98 + oam = nil 99 + return fmt.Errorf("`code` is not in response_types_supported") 100 + } 101 + 102 + if !tokenInSet("authorization_code", oam.GrantTypesSupported) { 103 + oam = nil 104 + return fmt.Errorf("`authorization_code` is not in grant_types_supported") 105 + } 106 + 107 + if !tokenInSet("refresh_token", oam.GrantTypesSupported) { 108 + oam = nil 109 + return fmt.Errorf("`refresh_token` is not in grant_types_supported") 110 + } 111 + 112 + if !tokenInSet("S256", oam.CodeChallengeMethodsSupported) { 113 + oam = nil 114 + return fmt.Errorf("`S256` is not in code_challenge_methods_supported") 115 + } 116 + 117 + if !tokenInSet("none", oam.TokenEndpointAuthMethodsSupported) { 118 + oam = nil 119 + return fmt.Errorf("`none` is not in token_endpoint_auth_methods_supported") 120 + } 121 + 122 + if !tokenInSet("private_key_jwt", oam.TokenEndpointAuthMethodsSupported) { 123 + oam = nil 124 + return fmt.Errorf("`private_key_jwt` is not in token_endpoint_auth_methods_supported") 125 + } 126 + 127 + if !tokenInSet("ES256", oam.TokenEndpointAuthSigningAlgValuesSupported) { 128 + oam = nil 129 + return fmt.Errorf("`ES256` is not in token_endpoint_auth_signing_alg_values_supported") 130 + } 131 + 132 + if !tokenInSet("atproto", oam.ScopesSupported) { 133 + oam = nil 134 + return fmt.Errorf("`atproto` is not in scopes_supported") 135 + } 136 + 137 + if oam.AuthorizationResponseISSParameterSupported != true { 138 + oam = nil 139 + return fmt.Errorf("authorization_response_iss_parameter_supported is not true") 140 + } 141 + 142 + if oam.PushedAuthorizationRequestEndpoint == "" { 143 + oam = nil 144 + return fmt.Errorf("pushed_authorization_request_endpoint is empty") 145 + } 146 + 147 + if oam.RequirePushedAuthorizationRequests == false { 148 + oam = nil 149 + return fmt.Errorf("require_pushed_authorization_requests is false") 150 + } 151 + 152 + if !tokenInSet("ES256", oam.DpopSigningAlgValuesSupported) { 153 + oam = nil 154 + return fmt.Errorf("`ES256` is not in dpop_signing_alg_values_supported") 155 + } 156 + 157 + if oam.RequireRequestUriRegistration != nil && *oam.RequireRequestUriRegistration == false { 158 + oam = nil 159 + return fmt.Errorf("require_request_uri_registration present in metadata and was false") 160 + } 161 + 162 + if oam.ClientIDMetadataDocumentSupported == false { 163 + oam = nil 164 + return fmt.Errorf("client_id_metadata_document_supported was false") 165 + } 166 + 167 + return nil 168 + } 169 + 170 + func (oam *OauthAuthorizationMetadata) UnmarshalJSON(b []byte) error { 171 + type Tmp OauthAuthorizationMetadata 172 + var tmp Tmp 173 + 174 + if err := json.Unmarshal(b, &tmp); err != nil { 175 + return err 176 + } 177 + 178 + *oam = OauthAuthorizationMetadata(tmp) 179 + 180 + return nil 181 + }