This commit is contained in:
commit
f867bf493e
8
.drone.yml
Normal file
8
.drone.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: gtslib-auth-keyring
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: golang:1.21
|
||||||
|
commands:
|
||||||
|
- go build -v ./...
|
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
gtslib-auth-keyring: GoToSocial client auth with keyring support
|
||||||
|
Copyright (C) 2024 decentral1se
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU Affero General Public License as published by the Free
|
||||||
|
Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
|
later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License along
|
||||||
|
with this program. If not, see <https://www.gnu.org/licenses/>.
|
32
README.md
Normal file
32
README.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# gtslib-auth-keyring
|
||||||
|
|
||||||
|
[](https://build.coopcloud.tech/decentral1se/gtslib-auth-keyring)
|
||||||
|
|
||||||
|
> [GoToSocial](https://gotosocial.org) client auth with keyring support
|
||||||
|
|
||||||
|
**ALPHA SOFTWARE** just like GoToSocial. Uses [`go-keyring`](https://github.com/zalando/go-keyring).
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
auth "git.coopcloud.tech/decentral1se/gtslib-auth-keyring"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := auth.Login("foo@bar.zone"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## ACK
|
||||||
|
|
||||||
|
Made possible by the good work of [`slurp`](https://github.com/VyrCossont/slurp).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
<a href="https://git.coopcloud.tech/decentral1se/gtslib-auth-keyring/src/branch/main/LICENSE">
|
||||||
|
<img src="https://www.gnu.org/graphics/agplv3-with-text-162x68.png" />
|
||||||
|
</a>
|
45
go.mod
Normal file
45
go.mod
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
module git.coopcloud.tech/decentral1se/gtslib-auth-keyring
|
||||||
|
|
||||||
|
go 1.21.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.coopcloud.tech/decentral1se/gtslib v0.0.0-20240731154340-f0c5a903cfbb
|
||||||
|
github.com/adrg/xdg v0.5.0
|
||||||
|
github.com/go-openapi/runtime v0.28.0
|
||||||
|
github.com/go-openapi/strfmt v0.23.0
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/zalando/go-keyring v0.2.5
|
||||||
|
golang.org/x/time v0.5.0
|
||||||
|
webfinger.net/go/webfinger v0.1.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alessio/shellescape v1.4.1 // indirect
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
|
github.com/danieljoos/wincred v1.2.0 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||||
|
github.com/go-openapi/errors v0.22.0 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/loads v0.22.0 // indirect
|
||||||
|
github.com/go-openapi/spec v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
|
github.com/go-openapi/validate v0.24.0 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||||
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
99
go.sum
Normal file
99
go.sum
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
git.coopcloud.tech/decentral1se/gtslib v0.0.0-20240731154340-f0c5a903cfbb h1:3wXXHqJENmkn/4B0fK8PZ2vjOMTC5K0Fp1Ca5sKROHQ=
|
||||||
|
git.coopcloud.tech/decentral1se/gtslib v0.0.0-20240731154340-f0c5a903cfbb/go.mod h1:isFkvbxjltfWUlzdVETbupmKLZvDf5XviBkgd2/+A5M=
|
||||||
|
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
|
||||||
|
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
|
||||||
|
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
|
||||||
|
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
|
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
|
||||||
|
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||||
|
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||||
|
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
|
||||||
|
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
|
||||||
|
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||||
|
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
|
||||||
|
github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
|
||||||
|
github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
|
||||||
|
github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
|
||||||
|
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||||
|
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||||
|
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
|
||||||
|
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
|
||||||
|
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||||
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
|
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
|
||||||
|
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||||
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||||
|
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
|
||||||
|
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||||
|
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
|
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||||
|
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||||
|
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||||
|
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||||
|
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||||
|
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||||
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
|
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
webfinger.net/go/webfinger v0.1.0 h1:e/J18UgjFE8+ZbKxzKm4+gv4ehidNnF6hcbHwS3K63U=
|
||||||
|
webfinger.net/go/webfinger v0.1.0/go.mod h1:+najbdnIKfnKo68tU2TF+AXm8/MOqLYXqx22j8Xw7FM=
|
552
gtslib_auth_keyring.go
Normal file
552
gtslib_auth_keyring.go
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
package gtslib_auth_keyring
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
neturl "net/url"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/adrg/xdg"
|
||||||
|
"github.com/go-openapi/runtime"
|
||||||
|
httptransport "github.com/go-openapi/runtime/client"
|
||||||
|
"github.com/go-openapi/strfmt"
|
||||||
|
"github.com/pkg/browser"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zalando/go-keyring"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
"webfinger.net/go/webfinger"
|
||||||
|
|
||||||
|
apiclient "git.coopcloud.tech/decentral1se/gtslib/client"
|
||||||
|
"git.coopcloud.tech/decentral1se/gtslib/client/apps"
|
||||||
|
"git.coopcloud.tech/decentral1se/gtslib/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Ptr[T any](v T) *T { return &v }
|
||||||
|
|
||||||
|
// Prefs stores persisted preferences.
|
||||||
|
type Prefs struct {
|
||||||
|
// Instances is a map of instance names to instance preferences.
|
||||||
|
Instances map[string]PrefsInstance `json:"instances,omitempty"`
|
||||||
|
|
||||||
|
// Users is a map of user usernames@domains to instance data.
|
||||||
|
Users map[string]PrefsUser `json:"users,omitempty"`
|
||||||
|
|
||||||
|
// DefaultUser is the username@domain of the last user we successfully
|
||||||
|
// authenticated as, if there is one.
|
||||||
|
DefaultUser string `json:"default_user,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefsInstance stores preferences for a given instance. OAuth2 app secrets
|
||||||
|
// are stored in the system keychain.
|
||||||
|
type PrefsInstance struct {
|
||||||
|
// ClientID is the OAuth2 client ID on this instance.
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefsUser stores preferences for a given user. User access tokens are stored
|
||||||
|
// in the system keychain.
|
||||||
|
type PrefsUser struct {
|
||||||
|
// Instance is the name of the instance that the user is on.
|
||||||
|
Instance string `json:"instance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// prefsDir is the path to the directory containing all preference files.
|
||||||
|
var prefsDir string
|
||||||
|
|
||||||
|
// prefsPath is the path to the file within that directory that stores all of our prefs.
|
||||||
|
var prefsPath string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
prefsDir = filepath.Join(xdg.ConfigHome, "gtslib.auth.keyring")
|
||||||
|
prefsPath = filepath.Join(prefsDir, "prefs.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPrefs returns preferences from disk or an empty prefs object if there
|
||||||
|
// are none stored or accessible.
|
||||||
|
func LoadPrefs() (*Prefs, error) {
|
||||||
|
f, err := os.Open(prefsPath)
|
||||||
|
if err != nil {
|
||||||
|
return &Prefs{
|
||||||
|
Instances: map[string]PrefsInstance{},
|
||||||
|
Users: map[string]PrefsUser{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
|
var prefs Prefs
|
||||||
|
err = json.NewDecoder(f).Decode(&prefs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefs.Instances == nil {
|
||||||
|
prefs.Instances = map[string]PrefsInstance{}
|
||||||
|
}
|
||||||
|
if prefs.Users == nil {
|
||||||
|
prefs.Users = map[string]PrefsUser{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &prefs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePrefs creates on-disk preferences or overwrites existing ones.
|
||||||
|
func SavePrefs(prefs *Prefs) error {
|
||||||
|
err := os.MkdirAll(prefsDir, 0o755)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Create(prefsPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
defer func() { _ = f.Close() }()
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(f)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
encoder.SetEscapeHTML(false)
|
||||||
|
err = encoder.Encode(prefs)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrefNotFound represents an error in which no preference is found.
|
||||||
|
var PrefNotFound = errors.New("preference value not found")
|
||||||
|
|
||||||
|
// getPrefValue retrieves a preference value.
|
||||||
|
func getPrefValue(get func(prefs *Prefs) (string, bool)) (string, error) {
|
||||||
|
prefs, err := LoadPrefs()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
value, exists := get(prefs)
|
||||||
|
if !exists {
|
||||||
|
return "", errors.WithStack(PrefNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPrefValue sets a preference value.
|
||||||
|
func setPrefValue(set func(prefs *Prefs)) error {
|
||||||
|
prefs, err := LoadPrefs()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
set(prefs)
|
||||||
|
|
||||||
|
err = SavePrefs(prefs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefaultUser retrieves the default user.
|
||||||
|
func GetDefaultUser() (string, error) {
|
||||||
|
return getPrefValue(func(prefs *Prefs) (string, bool) {
|
||||||
|
if prefs.DefaultUser == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return prefs.DefaultUser, true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultUser sets the default user.
|
||||||
|
func SetDefaultUser(user string) error {
|
||||||
|
return setPrefValue(func(prefs *Prefs) {
|
||||||
|
prefs.DefaultUser = user
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstanceClientID retrieves the instance client ID.
|
||||||
|
func GetInstanceClientID(instance string) (string, error) {
|
||||||
|
return getPrefValue(func(prefs *Prefs) (string, bool) {
|
||||||
|
prefsInstance, exists := prefs.Instances[instance]
|
||||||
|
if !exists {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return prefsInstance.ClientID, true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInstanceClientID sets the instance client ID.
|
||||||
|
func SetInstanceClientID(instance string, clientID string) error {
|
||||||
|
return setPrefValue(func(prefs *Prefs) {
|
||||||
|
prefsInstance := prefs.Instances[instance]
|
||||||
|
prefsInstance.ClientID = clientID
|
||||||
|
prefs.Instances[instance] = prefsInstance
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserInstance gets the user instance.
|
||||||
|
func GetUserInstance(user string) (string, error) {
|
||||||
|
return getPrefValue(func(prefs *Prefs) (string, bool) {
|
||||||
|
prefsUser, exists := prefs.Users[user]
|
||||||
|
if !exists {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return prefsUser.Instance, true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserInstance sets the user instance.
|
||||||
|
func SetUserInstance(user string, instance string) error {
|
||||||
|
return setPrefValue(func(prefs *Prefs) {
|
||||||
|
prefsUser := prefs.Users[user]
|
||||||
|
prefsUser.Instance = instance
|
||||||
|
prefs.Users[user] = prefsUser
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is a GtS API client with attached authentication credentials and rate
|
||||||
|
// limiter. Credentials may be no-op.
|
||||||
|
type Client struct {
|
||||||
|
Client *apiclient.GoToSocialSwaggerDocumentation
|
||||||
|
Auth runtime.ClientAuthInfoWriter
|
||||||
|
limiter *rate.Limiter
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Wait() error {
|
||||||
|
if err := c.limiter.Wait(c.ctx); err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthClient creates a new authentication client.
|
||||||
|
func NewAuthClient(user string) (*Client, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if user == "" {
|
||||||
|
user, err = GetDefaultUser()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("no user provided, couldn't get default user from prefs (did you log in first?)")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := GetUserInstance(user)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't get user's instance from prefs (did you log in first?)", "user", user)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
accessToken, err := keyring.Get(keyringServiceAccessToken, user)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't find user's access token (did you log in first?)", "user", user)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
Client: clientForInstance(instance),
|
||||||
|
Auth: httptransport.BearerToken(accessToken),
|
||||||
|
limiter: rate.NewLimiter(1.0, 300),
|
||||||
|
ctx: context.Background(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyringServiceAccessToken = "gtslib.auth.keyring.access-token"
|
||||||
|
keyringServiceClientSecret = "gtslib.auth.keyring.client-secret"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
oauthRedirect = "urn:ietf:wg:oauth:2.0:oob"
|
||||||
|
oauthScopes = "read write"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Login authenticates the user and saves the credentials in the system
|
||||||
|
// keychain.
|
||||||
|
func Login(user string) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if user == "" {
|
||||||
|
user, err = GetDefaultUser()
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("no user provided, couldn't get default user from prefs (have you logged in before?)")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if user == "" {
|
||||||
|
return errors.WithStack(errors.New("a user is required"))
|
||||||
|
}
|
||||||
|
if !strings.ContainsRune(user, '@') {
|
||||||
|
return errors.WithStack(errors.New("a fully qualified user with a domain is required"))
|
||||||
|
}
|
||||||
|
if user[0] == '@' {
|
||||||
|
return errors.WithStack(errors.New("take the leading @ off the user and try again"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := keyring.Get(keyringServiceAccessToken, user); err == nil {
|
||||||
|
slog.Warn("already logged in, will log in again", "user", user)
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := ensureInstance(user)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't get user's instance", "user", user, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client := clientForInstance(instance)
|
||||||
|
clientID, clientSecret, err := ensureAppCredentials(instance, client)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("OAuth2 app setup failed", "user", user, "instance", instance, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
code := promptForOAuthCode(instance, clientID)
|
||||||
|
|
||||||
|
accessToken, err := exchangeCodeForToken(instance, clientID, clientSecret, code)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't exchange OAuth2 authorization code for access token", "user", user, "instance", instance, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = keyring.Set(keyringServiceAccessToken, user, accessToken)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't set access token in keychain", "user", user, "instance", instance, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = SetDefaultUser(user)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't set default user in prefs", "user", user, "instance", instance, "error", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("login successful", "user", user, "instance", instance)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureInstance finds a user's instance or retrieves a previously cached
|
||||||
|
// instance for them.
|
||||||
|
func ensureInstance(user string) (string, error) {
|
||||||
|
if instance, err := GetUserInstance(user); err == nil {
|
||||||
|
return instance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, err := findInstance(user)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("WebFinger lookup failed", "user", user, "error", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = SetUserInstance(user, instance)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't set instance in prefs", "user", user, "instance", instance, "error", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findInstance does a WebFinger lookup to find the domain of the instance API
|
||||||
|
// for a given user.
|
||||||
|
func findInstance(user string) (string, error) {
|
||||||
|
webfingerClient := webfinger.NewClient(nil)
|
||||||
|
jrd, err := webfingerClient.Lookup(user, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var href string
|
||||||
|
for _, link := range jrd.Links {
|
||||||
|
if link.Rel == "self" && link.Type == "application/activity+json" {
|
||||||
|
href = link.Href
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if href == "" {
|
||||||
|
return "", errors.New("no link with rel=\"self\" and type=\"application/activity+json\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
url, err := neturl.Parse(href)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if url.Scheme != "https" || !(url.Port() == "" || url.Port() == "443") || url.Hostname() == "" {
|
||||||
|
return "", errors.New("unexpected URL format")
|
||||||
|
}
|
||||||
|
|
||||||
|
return url.Hostname(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientForInstance retrieves the client for a specific instance.
|
||||||
|
func clientForInstance(instance string) *apiclient.GoToSocialSwaggerDocumentation {
|
||||||
|
return apiclient.New(httptransport.New(instance, "", []string{"https"}), strfmt.Default)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureAppCredentials retrieves or creates and stores app credentials.
|
||||||
|
func ensureAppCredentials(instance string, client *apiclient.GoToSocialSwaggerDocumentation) (string, string, error) {
|
||||||
|
shouldCreateNewApp := false
|
||||||
|
|
||||||
|
clientID, err := GetInstanceClientID(instance)
|
||||||
|
if clientID == "" || errors.Is(err, keyring.ErrNotFound) {
|
||||||
|
shouldCreateNewApp = true
|
||||||
|
} else if err != nil {
|
||||||
|
slog.Error("couldn't get client ID from prefs", "instance", instance, "error", err)
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientSecret, err := keyring.Get(keyringServiceClientSecret, instance)
|
||||||
|
if clientSecret == "" || errors.Is(err, keyring.ErrNotFound) {
|
||||||
|
shouldCreateNewApp = true
|
||||||
|
} else if err != nil {
|
||||||
|
slog.Error("couldn't get client secret from keychain", "instance", instance, "error", err)
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !shouldCreateNewApp {
|
||||||
|
return clientID, clientSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := createApp(client)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't create OAuth2 app", "instance", instance, "error", err)
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
clientID = app.ClientID
|
||||||
|
clientSecret = app.ClientSecret
|
||||||
|
|
||||||
|
err = SetInstanceClientID(instance, clientID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't set client ID in prefs", "instance", instance, "error", err)
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
err = keyring.Set(keyringServiceClientSecret, instance, clientSecret)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't set client secret in keychain", "instance", instance, "error", err)
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientID, clientSecret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createApp registers a new OAuth2 application.
|
||||||
|
func createApp(client *apiclient.GoToSocialSwaggerDocumentation) (*models.Application, error) {
|
||||||
|
resp, err := client.Apps.AppCreate(
|
||||||
|
&apps.AppCreateParams{
|
||||||
|
ClientName: "gtslib",
|
||||||
|
RedirectURIs: oauthRedirect,
|
||||||
|
Scopes: Ptr(oauthScopes),
|
||||||
|
Website: Ptr("https://foo.com/gtslib"),
|
||||||
|
},
|
||||||
|
func(op *runtime.ClientOperation) {
|
||||||
|
op.ConsumesMediaTypes = []string{"application/x-www-form-urlencoded"}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.GetPayload(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// promptForOAuthCode prompts for an OAuth code.
|
||||||
|
func promptForOAuthCode(instance string, clientID string) string {
|
||||||
|
oauthAuthorizeURL := (&neturl.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: instance,
|
||||||
|
Path: "/oauth/authorize",
|
||||||
|
RawQuery: neturl.Values{
|
||||||
|
"response_type": []string{"code"},
|
||||||
|
"client_id": []string{clientID},
|
||||||
|
"redirect_uri": []string{oauthRedirect},
|
||||||
|
"scope": []string{oauthScopes},
|
||||||
|
}.Encode(),
|
||||||
|
}).String()
|
||||||
|
err := browser.OpenURL(oauthAuthorizeURL)
|
||||||
|
if err != nil {
|
||||||
|
slog.Warn("couldn't open browser to authorize", "error", err)
|
||||||
|
print("Please open this URL in your browser:", oauthAuthorizeURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Enter authorization code: ")
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
scanner.Scan()
|
||||||
|
code := strings.TrimSpace(scanner.Text())
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
type oauthTokenOK struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type oauthTokenError struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// exchangeCodeForToken exchanges an authorization code for an access token.
|
||||||
|
func exchangeCodeForToken(instance string, clientID string, clientSecret string, code string) (string, error) {
|
||||||
|
oauthTokenURL := (&neturl.URL{
|
||||||
|
Scheme: "https",
|
||||||
|
Host: instance,
|
||||||
|
Path: "/oauth/token",
|
||||||
|
}).String()
|
||||||
|
|
||||||
|
// TODO: add this to GtS Swagger doc
|
||||||
|
resp, err := http.Post(oauthTokenURL, "application/x-www-form-urlencoded", strings.NewReader(neturl.Values{
|
||||||
|
"grant_type": []string{"authorization_code"},
|
||||||
|
"code": []string{code},
|
||||||
|
"client_id": []string{clientID},
|
||||||
|
"client_secret": []string{clientSecret},
|
||||||
|
"redirect_uri": []string{oauthRedirect},
|
||||||
|
"scope": []string{oauthScopes},
|
||||||
|
}.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("call to OAuth2 token endpoint failed", "instance", instance, "error", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
var payload oauthTokenError
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&payload)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't decode OAuth2 token endpoint error response", "instance", instance, "error", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", errors.WithStack(errors.New(payload.ErrorDescription))
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload oauthTokenOK
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&payload)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("couldn't decode OAuth2 token endpoint success response", "instance", instance, "error", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.TokenType != "Bearer" {
|
||||||
|
err = errors.WithStack(errors.New("unknown access token type"))
|
||||||
|
slog.Error("unexpected response from OAuth2 token endpoint", "instance", instance, "token_type", payload.TokenType)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.Scope != oauthScopes {
|
||||||
|
err = errors.WithStack(errors.New("scopes are not what we asked for"))
|
||||||
|
slog.Error("unexpected response from OAuth2 token endpoint", "instance", instance, "scopes", payload.Scope)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload.AccessToken, nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user