From 4f9994bf792c3887f2f45910b11a9c19ee3a896b Mon Sep 17 00:00:00 2001 From: Will Matz <39891237+william-matz@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:20:36 -0400 Subject: [PATCH 01/42] feat: use largest avatar from spotify instead (#1210) Extracts the largest avatar returned by Spotify, instead of the first avatar in the list. Fixes: - #1209 --- internal/api/provider/spotify.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/api/provider/spotify.go b/internal/api/provider/spotify.go index 57fe8c0f8..ebd226fc6 100644 --- a/internal/api/provider/spotify.go +++ b/internal/api/provider/spotify.go @@ -86,8 +86,17 @@ func (g spotifyProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*U var avatarURL string + // Spotify returns a list of avatars, we want to use the largest one if len(u.Avatars) >= 1 { - avatarURL = u.Avatars[0].Url + largestAvatar := u.Avatars[0] + + for _, avatar := range u.Avatars { + if avatar.Height * avatar.Width > largestAvatar.Height * largestAvatar.Width { + largestAvatar = avatar + } + } + + avatarURL = largestAvatar.Url } data.Metadata = &Claims{ From f9c13c0ad5c556bede49d3e0f6e5f58ca26161c3 Mon Sep 17 00:00:00 2001 From: Lasha <72510037+LashaJini@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:55:37 +0400 Subject: [PATCH 02/42] feat: add max length check for email (#1508) ## What kind of change does this PR introduce? feature: add max length check for email. ## What is the current behavior? Currently, email length is only checked on db side. Email has max length 255 characters, when user sends (>255 characters) large email to `/admin/users` endpoint, db is doing unnecessary queries. ![Screenshot from 2024-03-30 02-40-54](https://github.com/supabase/auth/assets/72510037/10a36b08-5112-4737-9c3a-b9e01c7ccc10) ## What is the new behavior? Code returns early if user enters large email. There will be no db queries. ![Screenshot from 2024-03-30 02-44-31](https://github.com/supabase/auth/assets/72510037/735a4e79-561f-412a-b536-6dac3aa6f339) --- internal/api/mail.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/api/mail.go b/internal/api/mail.go index 86c31f56a..30f358ad2 100644 --- a/internal/api/mail.go +++ b/internal/api/mail.go @@ -550,6 +550,9 @@ func validateEmail(email string) (string, error) { if email == "" { return "", badRequestError(ErrorCodeValidationFailed, "An email address is required") } + if len(email) > 255 { + return "", badRequestError(ErrorCodeValidationFailed, "An email address is too long") + } if err := checkmail.ValidateFormat(email); err != nil { return "", badRequestError(ErrorCodeValidationFailed, "Unable to validate email address: "+err.Error()) } From fa90764847f625d11bc52d3b3f3a0c40c6548eb0 Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Thu, 6 Jun 2024 18:41:16 +0200 Subject: [PATCH 03/42] ci: re-format files (#1611) Re-formats the files from PR #1209. --- internal/api/provider/spotify.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/api/provider/spotify.go b/internal/api/provider/spotify.go index ebd226fc6..e6d2f383c 100644 --- a/internal/api/provider/spotify.go +++ b/internal/api/provider/spotify.go @@ -89,13 +89,13 @@ func (g spotifyProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*U // Spotify returns a list of avatars, we want to use the largest one if len(u.Avatars) >= 1 { largestAvatar := u.Avatars[0] - + for _, avatar := range u.Avatars { - if avatar.Height * avatar.Width > largestAvatar.Height * largestAvatar.Width { + if avatar.Height*avatar.Width > largestAvatar.Height*largestAvatar.Width { largestAvatar = avatar } } - + avatarURL = largestAvatar.Url } From cdd13adec02eb0c9401bc55a2915c1005d50dea1 Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Fri, 7 Jun 2024 13:54:21 +0200 Subject: [PATCH 04/42] feat: upgrade otel to v1.26 (#1585) ## What kind of change does this PR introduce? Spiritual successor to #1305 where we attempt to bump the OTEL version from v1.10.0 to v1.26.0 which has a few breaking changes ## How this was tested We used a local setup with honeycomb with tracing and metrics enabled. We then triggered - a cleanup to test `gotrue_cleanup_affected_rows` - a few password sign ins to test `gotrue_compare_hash_and_password_submitted` - Checked if `gotrue_running` was present - Checked that DB traces were still present DB Trace: CleanShot 2024-05-23 at 20 46 02@2x Metrics: CleanShot 2024-05-23 at 20 41 51@2x --- go.mod | 77 ++- go.sum | 575 +++------------------- internal/api/middleware.go | 3 +- internal/api/opentelemetry-tracer_test.go | 12 +- internal/crypto/password.go | 13 +- internal/models/cleanup.go | 28 +- internal/observability/metrics.go | 136 +++-- internal/observability/request-tracing.go | 28 +- 8 files changed, 219 insertions(+), 653 deletions(-) diff --git a/go.mod b/go.mod index 02c99607d..3911c8a69 100644 --- a/go.mod +++ b/go.mod @@ -28,9 +28,9 @@ require ( github.com/sethvargo/go-password v0.2.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.6.1 - github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.23.0 - golang.org/x/oauth2 v0.7.0 + github.com/stretchr/testify v1.9.0 + golang.org/x/crypto v0.21.0 + golang.org/x/oauth2 v0.17.0 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df ) @@ -40,25 +40,23 @@ require ( github.com/gobuffalo/nulls v0.4.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + golang.org/x/mod v0.9.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect ) require ( - github.com/XSAM/otelsql v0.16.0 + github.com/XSAM/otelsql v0.26.0 github.com/bombsimon/logrusr/v3 v3.0.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.36.0 - go.opentelemetry.io/contrib/instrumentation/runtime v0.35.0 - go.opentelemetry.io/otel v1.10.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.31.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.10.0 - go.opentelemetry.io/otel/exporters/prometheus v0.31.0 - go.opentelemetry.io/otel/metric v0.32.0 - go.opentelemetry.io/otel/sdk v1.10.0 - go.opentelemetry.io/otel/sdk/metric v0.31.0 - go.opentelemetry.io/otel/trace v1.10.0 + go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0 + go.opentelemetry.io/otel v1.26.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 + go.opentelemetry.io/otel/metric v1.26.0 + go.opentelemetry.io/otel/sdk v1.26.0 + go.opentelemetry.io/otel/sdk/metric v1.26.0 + go.opentelemetry.io/otel/trace v1.26.0 gopkg.in/h2non/gock.v1 v1.1.2 ) @@ -74,6 +72,10 @@ require ( github.com/supabase/hibp v0.0.0-20231124125943-d225752ae869 github.com/supabase/mailme v0.2.0 github.com/xeipuuv/gojsonschema v1.2.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.26.0 + go.opentelemetry.io/otel/exporters/prometheus v0.48.0 ) require ( @@ -81,13 +83,13 @@ require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/beevik/etree v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/crewjam/httperr v0.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.13.0 // indirect - github.com/felixge/httpsnoop v1.0.3 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/gobuffalo/envy v1.10.2 // indirect @@ -98,10 +100,10 @@ require ( github.com/gobuffalo/plush/v4 v4.1.18 // indirect github.com/gobuffalo/tags/v3 v3.1.4 // indirect github.com/golang-jwt/jwt/v4 v4.4.3 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/css v1.0.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect @@ -116,31 +118,28 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.12.2 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect - github.com/prometheus/procfs v0.7.3 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/prometheus/client_golang v1.19.0 + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/russellhaering/goxmldsig v1.3.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb golang.org/x/net v0.23.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.56.3 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/grpc v1.63.2 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 20fa41a75..fdfb47a9b 100644 --- a/go.sum +++ b/go.sum @@ -1,56 +1,15 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= -github.com/XSAM/otelsql v0.16.0 h1:pOqeHGYCJmP5ezW0OvAGA+zzdgW/sV8nLHTxVnPgiXU= -github.com/XSAM/otelsql v0.16.0/go.mod h1:DpO7NCSeqQdr23nU0yapjR3jGx2OdO/PihPRG+/PV0Y= +github.com/XSAM/otelsql v0.26.0 h1:UhAGVBD34Ctbh2aYcm/JAdL+6T6ybrP+YMWYkHqCdmo= +github.com/XSAM/otelsql v0.26.0/go.mod h1:5ciw61eMSh+RtTPN8spvPEPLJpAErZw8mFFPNfYiaxA= github.com/aaronarduino/goqrsvg v0.0.0-20220419053939-17e843f1dd40 h1:uz4N2yHL4MF8vZX+36n+tcxeUf8D/gL4aJkyouhDw4A= github.com/aaronarduino/goqrsvg v0.0.0-20220419053939-17e843f1dd40/go.mod h1:dytw+5qs+pdi61fO/S4OmXR7AuEq/HvNCuG03KxQHT4= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= @@ -59,10 +18,6 @@ github.com/badoux/checkmail v0.0.0-20170203135005-d0a759655d62 h1:vMqcPzLT1/mbYe github.com/badoux/checkmail v0.0.0-20170203135005-d0a759655d62/go.mod h1:r5ZalvRl3tXevRNJkwIB6DC4DD3DMjIlY9NEU1XGoaQ= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= @@ -74,25 +29,10 @@ github.com/bombsimon/logrusr/v3 v3.0.0 h1:tcAoLfuAhKP9npBxWzSdpsvKPQt1XV02nSf2lZ github.com/bombsimon/logrusr/v3 v3.0.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHAnFF5E+g8Ixco= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= @@ -113,36 +53,21 @@ github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= github.com/didip/tollbooth/v5 v5.1.1 h1:QpKFg56jsbNuQ6FFj++Z1gn2fbBsvAc1ZPLUaDOYW5k= github.com/didip/tollbooth/v5 v5.1.1/go.mod h1:d9rzwOULswrD3YIrAQmP3bfjxab32Df4IaO6+D25l9g= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/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-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -182,83 +107,27 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +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/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -321,16 +190,7 @@ github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= @@ -338,8 +198,6 @@ github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -376,29 +234,19 @@ github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58= github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/patrickmn/go-cache v0.0.0-20170418232947-7ac151875ffb/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -406,34 +254,20 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -452,10 +286,8 @@ github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetS github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -463,7 +295,6 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9 github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -475,8 +306,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -486,8 +318,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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/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/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/supabase/hibp v0.0.0-20231124125943-d225752ae869 h1:VDuRtwen5Z7QQ5ctuHUse4wAv/JozkKZkdic5vUV4Lg= github.com/supabase/hibp v0.0.0-20231124125943-d225752ae869/go.mod h1:eHX5nlSMSnyPjUrbYzeqrA8snCe2SKyfizKjU3dkfOw= github.com/supabase/mailme v0.2.0 h1:39LHZ4+YOeqoN4MiuncPBC3JarExAa0flmokM24qHNU= @@ -500,56 +332,43 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.36.0 h1:qZ3KzA4qPzLBDtQyPk4ydjlg8zvXbNysnFHaVMKJbVo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.36.0/go.mod h1:14Oo79mRwusSI02L0EfG3Gp1uF3+1wSL+D4zDysxyqs= -go.opentelemetry.io/contrib/instrumentation/runtime v0.35.0 h1:VvinIr6FpeUnW0ATkV+NBPT+bMkytiqRuA6Hj+t4Ylc= -go.opentelemetry.io/contrib/instrumentation/runtime v0.35.0/go.mod h1:7iORGR19PYmn3eq0kEI4qAVdzeXcZiqYyS2pGUnVXXU= -go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4= -go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0 h1:TaB+1rQhddO1sF71MpZOZAuSPW1klK2M8XxfrBMfK7Y= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0 h1:H0+xwv4shKw0gfj/ZqR13qO2N/dBQogB1OcRjJjV39Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.31.0/go.mod h1:nkenGD8vcvs0uN6WhR90ZVHQlgDsRmXicnNadMnk+XQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0 h1:BaQ2xM5cPmldVCMvbLoy5tcLUhXCtIhItDYBNw83B7Y= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.31.0/go.mod h1:VRr8tlXQEsTdesDCh0qBe2iKDWhpi3ZqDYw6VlZ8MhI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.31.0 h1:MuEG0gG27QZQrqhNl0f7vQ5Nl03OQfFeDAqWkGt+1zM= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.31.0/go.mod h1:52qtPFDDaa0FaSyyzPnxWMehx2SZv0xuobTlNEZA2JA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0 h1:pDDYmo0QadUPal5fwXoY1pmMpFcdyhXOmL5drCrI3vU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0 h1:KtiUEhQmj/Pa874bVYKGNVdq8NPKiacPbaRRtgXi+t4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.10.0 h1:S8DedULB3gp93Rh+9Z+7NTEv+6Id/KYS7LDyipZ9iCE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.10.0/go.mod h1:5WV40MLWwvWlGP7Xm8g3pMcg0pKOUY609qxJn8y7LmM= -go.opentelemetry.io/otel/exporters/prometheus v0.31.0 h1:jwtnOGBM8dIty5AVZ+9ZCzZexCea3aVKmUfZAQcHqxs= -go.opentelemetry.io/otel/exporters/prometheus v0.31.0/go.mod h1:QarXIB8L79IwIPoNgG3A6zNvBgVmcppeFogV1d8612s= -go.opentelemetry.io/otel/metric v0.32.0 h1:lh5KMDB8xlMM4kwE38vlZJ3rZeiWrjw3As1vclfC01k= -go.opentelemetry.io/otel/metric v0.32.0/go.mod h1:PVDNTt297p8ehm949jsIzd+Z2bIZJYQQG/uuHTeWFHY= -go.opentelemetry.io/otel/sdk v1.10.0 h1:jZ6K7sVn04kk/3DNUdJ4mqRlGDiXAVuIG+MMENpTNdY= -go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= -go.opentelemetry.io/otel/sdk/metric v0.31.0 h1:2sZx4R43ZMhJdteKAlKoHvRgrMp53V1aRxvEf5lCq8Q= -go.opentelemetry.io/otel/sdk/metric v0.31.0/go.mod h1:fl0SmNnX9mN9xgU6OLYLMBMrNAsaZQi7qBwprwO3abk= -go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E= -go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0/go.mod h1:vy+2G/6NvVMpwGX/NyLqcC41fxepnuKHk16E6IZUcJc= +go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0 h1:2JydY5UiDpqvj2p7sO9bgHuhTy4hgTZ0ymehdq/Ob0Q= +go.opentelemetry.io/contrib/instrumentation/runtime v0.45.0/go.mod h1:ch3a5QxOqVWxas4CzjCFFOOQe+7HgAXC/N1oVxS9DK4= +go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0 h1:+hm+I+KigBy3M24/h1p/NHkUx/evbLH0PNcjpMyCHc4= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0/go.mod h1:NjC8142mLvvNT6biDpaMjyz78kyEHIwAJlSX0N9P5KI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.26.0 h1:HGZWGmCVRCVyAs2GQaiHQPbDHo+ObFWeUEOd+zDnp64= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.26.0/go.mod h1:SaH+v38LSCHddyk7RGlU9uZyQoRrKao6IBnJw6Kbn+c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/exporters/prometheus v0.48.0 h1:sBQe3VNGUjY9IKWQC6z2lNqa5iGbDSxhs60ABwK4y0s= +go.opentelemetry.io/otel/exporters/prometheus v0.48.0/go.mod h1:DtrbMzoZWwQHyrQmCfLam5DZbnmorsGbOtTbYHycU5o= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= +go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8= +go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= +go.opentelemetry.io/otel/sdk/metric v1.26.0 h1:cWSks5tfriHPdWFnl+qpX3P681aAYqlZHcAyHw5aU9Y= +go.opentelemetry.io/otel/sdk/metric v1.26.0/go.mod h1:ClMFFknnThJCksebJwz7KIyEDHO+nTB6gK8obLy8RyE= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -557,11 +376,9 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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= @@ -571,76 +388,25 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w= golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20161007143504-f4b625ec9b21/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= @@ -649,76 +415,31 @@ 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.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -727,8 +448,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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= @@ -736,71 +457,29 @@ golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuX 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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20160926182426-711ca1cb8763/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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= @@ -810,97 +489,20 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -914,12 +516,8 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkp gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -928,14 +526,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/api/middleware.go b/internal/api/middleware.go index 291eba5e8..d4e3068eb 100644 --- a/internal/api/middleware.go +++ b/internal/api/middleware.go @@ -16,6 +16,7 @@ import ( "github.com/supabase/auth/internal/observability" "github.com/supabase/auth/internal/security" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" "github.com/didip/tollbooth/v5" "github.com/didip/tollbooth/v5/limiter" @@ -113,7 +114,7 @@ func (a *API) limitEmailOrPhoneSentHandler() middlewareHandler { emailRateLimitCounter.Add( req.Context(), 1, - attribute.String("path", req.URL.Path), + metric.WithAttributeSet(attribute.NewSet(attribute.String("path", req.URL.Path))), ) return c, tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, "Email rate limit exceeded") } diff --git a/internal/api/opentelemetry-tracer_test.go b/internal/api/opentelemetry-tracer_test.go index 0abcf869e..4aeddce5a 100644 --- a/internal/api/opentelemetry-tracer_test.go +++ b/internal/api/opentelemetry-tracer_test.go @@ -14,7 +14,7 @@ import ( "go.opentelemetry.io/otel/attribute" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" - semconv "go.opentelemetry.io/otel/semconv/v1.10.0" + semconv "go.opentelemetry.io/otel/semconv/v1.25.0" ) type OpenTelemetryTracerTestSuite struct { @@ -78,22 +78,16 @@ func (ts *OpenTelemetryTracerTestSuite) TestOpenTelemetryTracer_Spans() { method1 := getAttribute(attributes1, semconv.HTTPMethodKey) assert.Equal(ts.T(), "POST", method1.AsString()) url1 := getAttribute(attributes1, semconv.HTTPTargetKey) - assert.Equal(ts.T(), "http://localhost/something1", url1.AsString()) + assert.Equal(ts.T(), "/something1", url1.AsString()) statusCode1 := getAttribute(attributes1, semconv.HTTPStatusCodeKey) assert.Equal(ts.T(), int64(404), statusCode1.AsInt64()) - userAgent1 := getAttribute(attributes1, semconv.HTTPUserAgentKey) - assert.Equal(ts.T(), "stripped", userAgent1.AsString()) - attributes2 := spans[1].Attributes() method2 := getAttribute(attributes2, semconv.HTTPMethodKey) assert.Equal(ts.T(), "GET", method2.AsString()) url2 := getAttribute(attributes2, semconv.HTTPTargetKey) - assert.Equal(ts.T(), "http://localhost/something2", url2.AsString()) + assert.Equal(ts.T(), "/something2", url2.AsString()) statusCode2 := getAttribute(attributes2, semconv.HTTPStatusCodeKey) assert.Equal(ts.T(), int64(404), statusCode2.AsInt64()) - - userAgent2 := getAttribute(attributes2, semconv.HTTPUserAgentKey) - assert.Equal(ts.T(), "stripped", userAgent2.AsString()) } } diff --git a/internal/crypto/password.go b/internal/crypto/password.go index 6341e4d2d..554daccaa 100644 --- a/internal/crypto/password.go +++ b/internal/crypto/password.go @@ -12,6 +12,7 @@ import ( "github.com/supabase/auth/internal/observability" "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" "golang.org/x/crypto/argon2" "golang.org/x/crypto/bcrypt" @@ -123,14 +124,14 @@ func compareHashAndPasswordArgon2(ctx context.Context, hash, password string) er attribute.Int("len", len(rawHash)), } - compareHashAndPasswordSubmittedCounter.Add(ctx, 1, attributes...) + compareHashAndPasswordSubmittedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) defer func() { attributes = append(attributes, attribute.Bool( "match", match, )) - compareHashAndPasswordCompletedCounter.Add(ctx, 1, attributes...) + compareHashAndPasswordCompletedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) }() switch alg { @@ -169,14 +170,14 @@ func CompareHashAndPassword(ctx context.Context, hash, password string) error { attribute.Int("bcrypt_cost", hashCost), } - compareHashAndPasswordSubmittedCounter.Add(ctx, 1, attributes...) + compareHashAndPasswordSubmittedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) defer func() { attributes = append(attributes, attribute.Bool( "match", !errors.Is(err, bcrypt.ErrMismatchedHashAndPassword), )) - compareHashAndPasswordCompletedCounter.Add(ctx, 1, attributes...) + compareHashAndPasswordCompletedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) }() err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) @@ -207,8 +208,8 @@ func GenerateFromPassword(ctx context.Context, password string) (string, error) attribute.Int("bcrypt_cost", hashCost), } - generateFromPasswordSubmittedCounter.Add(ctx, 1, attributes...) - defer generateFromPasswordCompletedCounter.Add(ctx, 1, attributes...) + generateFromPasswordSubmittedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) + defer generateFromPasswordCompletedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) hash, err := bcrypt.GenerateFromPassword([]byte(password), hashCost) if err != nil { diff --git a/internal/models/cleanup.go b/internal/models/cleanup.go index 9da6363eb..46d4c2bdd 100644 --- a/internal/models/cleanup.go +++ b/internal/models/cleanup.go @@ -1,14 +1,14 @@ package models import ( + "context" "fmt" + "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" "sync/atomic" - "github.com/sirupsen/logrus" "go.opentelemetry.io/otel/attribute" - metricglobal "go.opentelemetry.io/otel/metric/global" - metricinstrument "go.opentelemetry.io/otel/metric/instrument" - otelasyncint64instrument "go.opentelemetry.io/otel/metric/instrument/asyncint64" "github.com/supabase/auth/internal/conf" "github.com/supabase/auth/internal/observability" @@ -24,7 +24,7 @@ type Cleanup struct { // cleanupAffectedRows tracks an OpenTelemetry metric on the total number of // cleaned up rows. - cleanupAffectedRows otelasyncint64instrument.Counter + cleanupAffectedRows atomic.Int64 } func NewCleanup(config *conf.GlobalConfiguration) *Cleanup { @@ -79,16 +79,21 @@ func NewCleanup(config *conf.GlobalConfiguration) *Cleanup { c.cleanupStatements = append(c.cleanupStatements, fmt.Sprintf("delete from %q where id in (select %q.id as id from %q, %q where %q.session_id = %q.id and %q.refreshed_at is null and %q.revoked is false and %q.updated_at + interval '%d seconds' < now() - interval '24 hours' limit 100 for update skip locked)", tableSessions, tableSessions, tableSessions, tableRefreshTokens, tableRefreshTokens, tableSessions, tableSessions, tableRefreshTokens, tableRefreshTokens, inactivitySeconds)) } - cleanupAffectedRows, err := metricglobal.Meter("gotrue").AsyncInt64().Counter( + meter := otel.Meter("gotrue") + + _, err := meter.Int64ObservableCounter( "gotrue_cleanup_affected_rows", - metricinstrument.WithDescription("Number of affected rows from cleaning up stale entities"), + metric.WithDescription("Number of affected rows from cleaning up stale entities"), + metric.WithInt64Callback(func(_ context.Context, o metric.Int64Observer) error { + o.Observe(c.cleanupAffectedRows.Load()) + return nil + }), ) + if err != nil { logrus.WithError(err).Error("unable to get gotrue.gotrue_cleanup_rows counter metric") } - c.cleanupAffectedRows = cleanupAffectedRows - return c } @@ -120,10 +125,7 @@ func (c *Cleanup) Clean(db *storage.Connection) (int, error) { }); err != nil { return affectedRows, err } - - if c.cleanupAffectedRows != nil { - c.cleanupAffectedRows.Observe(ctx, int64(affectedRows)) - } + c.cleanupAffectedRows.Add(int64(affectedRows)) return affectedRows, nil } diff --git a/internal/observability/metrics.go b/internal/observability/metrics.go index cfe8f2c8e..b3632aa8e 100644 --- a/internal/observability/metrics.go +++ b/internal/observability/metrics.go @@ -11,50 +11,38 @@ import ( "github.com/sirupsen/logrus" "github.com/supabase/auth/internal/conf" - "go.opentelemetry.io/otel/exporters/otlp/otlpmetric" + "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc" "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp" "go.opentelemetry.io/otel/exporters/prometheus" "go.opentelemetry.io/otel/metric" - metricglobal "go.opentelemetry.io/otel/metric/global" - metricinstrument "go.opentelemetry.io/otel/metric/instrument" - basicmetriccontroller "go.opentelemetry.io/otel/sdk/metric/controller/basic" - exportmetricaggregation "go.opentelemetry.io/otel/sdk/metric/export/aggregation" - basicmetricprocessor "go.opentelemetry.io/otel/sdk/metric/processor/basic" - simplemetricselector "go.opentelemetry.io/otel/sdk/metric/selector/simple" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" otelruntimemetrics "go.opentelemetry.io/contrib/instrumentation/runtime" ) func Meter(instrumentationName string, opts ...metric.MeterOption) metric.Meter { - return metricglobal.Meter(instrumentationName, opts...) + return otel.Meter(instrumentationName, opts...) } -func ObtainMetricCounter(name, desc string) metricCounter { - counter, err := Meter("gotrue").SyncInt64().Counter(name, metricinstrument.WithDescription(desc)) +func ObtainMetricCounter(name, desc string) metric.Int64Counter { + counter, err := Meter("gotrue").Int64Counter(name, metric.WithDescription(desc)) if err != nil { panic(err) } - return counter } func enablePrometheusMetrics(ctx context.Context, mc *conf.MetricsConfig) error { - controller := basicmetriccontroller.New( - basicmetricprocessor.NewFactory( - simplemetricselector.NewWithHistogramDistribution(), - exportmetricaggregation.CumulativeTemporalitySelector(), - basicmetricprocessor.WithMemory(true), // pushes all metrics, not only the collected ones - ), - basicmetriccontroller.WithResource(openTelemetryResource()), - ) - - exporter, err := prometheus.New(prometheus.Config{}, controller) + exporter, err := prometheus.New() if err != nil { return err } - metricglobal.SetMeterProvider(exporter.MeterProvider()) + provider := sdkmetric.NewMeterProvider(sdkmetric.WithReader(exporter)) + + otel.SetMeterProvider(provider) cleanupWaitGroup.Add(1) go func() { @@ -63,7 +51,7 @@ func enablePrometheusMetrics(ctx context.Context, mc *conf.MetricsConfig) error server := &http.Server{ Addr: addr, - Handler: exporter, + Handler: promhttp.Handler(), BaseContext: func(net.Listener) context.Context { return baseContext }, @@ -97,62 +85,67 @@ func enablePrometheusMetrics(ctx context.Context, mc *conf.MetricsConfig) error } func enableOpenTelemetryMetrics(ctx context.Context, mc *conf.MetricsConfig) error { - var ( - err error - metricExporter *otlpmetric.Exporter - ) - switch mc.ExporterProtocol { case "grpc": - metricExporter, err = otlpmetricgrpc.New(ctx) + metricExporter, err := otlpmetricgrpc.New(ctx) if err != nil { return err } + meterProvider := sdkmetric.NewMeterProvider( + sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExporter)), + ) + + otel.SetMeterProvider(meterProvider) + + cleanupWaitGroup.Add(1) + go func() { + defer cleanupWaitGroup.Done() + + <-ctx.Done() + + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer shutdownCancel() + + if err := metricExporter.Shutdown(shutdownCtx); err != nil { + logrus.WithError(err).Error("unable to gracefully shut down OpenTelemetry metric exporter") + } else { + logrus.Info("OpenTelemetry metric exporter shut down") + } + }() case "http/protobuf": - metricExporter, err = otlpmetrichttp.New(ctx) + metricExporter, err := otlpmetrichttp.New(ctx) if err != nil { return err } + meterProvider := sdkmetric.NewMeterProvider( + sdkmetric.WithReader(sdkmetric.NewPeriodicReader(metricExporter)), + ) - default: // http/json for example - return fmt.Errorf("unsupported OpenTelemetry exporter protocol %q", mc.ExporterProtocol) - } - - controller := basicmetriccontroller.New( - basicmetricprocessor.NewFactory( - simplemetricselector.NewWithHistogramDistribution(), - metricExporter, - ), - basicmetriccontroller.WithExporter(metricExporter), - basicmetriccontroller.WithResource(openTelemetryResource()), - ) - - metricglobal.SetMeterProvider(controller) + otel.SetMeterProvider(meterProvider) - cleanupWaitGroup.Add(1) - go func() { - defer cleanupWaitGroup.Done() + cleanupWaitGroup.Add(1) + go func() { + defer cleanupWaitGroup.Done() - <-ctx.Done() + <-ctx.Done() - shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second) - defer shutdownCancel() + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer shutdownCancel() - if err := metricExporter.Shutdown(shutdownCtx); err != nil { - logrus.WithError(err).Error("unable to gracefully shut down OpenTelemetry metric exporter") - } else { - logrus.Info("OpenTelemetry metric exporter shut down") - } - }() + if err := metricExporter.Shutdown(shutdownCtx); err != nil { + logrus.WithError(err).Error("unable to gracefully shut down OpenTelemetry metric exporter") + } else { + logrus.Info("OpenTelemetry metric exporter shut down") + } + }() - if err := controller.Start(ctx); err != nil { - logrus.WithError(err).Error("unable to start pushing OpenTelemetry metrics") - } else { - logrus.Info("OpenTelemetry metrics exporter started") + default: // http/json for example + return fmt.Errorf("unsupported OpenTelemetry exporter protocol %q", mc.ExporterProtocol) } - + logrus.Info("OpenTelemetry metrics exporter started") return nil + } var ( @@ -190,26 +183,19 @@ func ConfigureMetrics(ctx context.Context, mc *conf.MetricsConfig) error { logrus.Info("Go runtime metrics collection started") } - meter := metricglobal.Meter("gotrue") - running, err := meter.AsyncInt64().Gauge( + meter := otel.Meter("gotrue") + _, err := meter.Int64ObservableGauge( "gotrue_running", - metricinstrument.WithDescription("Whether GoTrue is running (always 1)"), + metric.WithDescription("Whether GoTrue is running (always 1)"), + metric.WithInt64Callback(func(_ context.Context, obsrv metric.Int64Observer) error { + obsrv.Observe(int64(1)) + return nil + }), ) if err != nil { logrus.WithError(err).Error("unable to get gotrue.gotrue_running gague metric") return } - - if err := meter.RegisterCallback( - []metricinstrument.Asynchronous{ - running, - }, - func(ctx context.Context) { - running.Observe(ctx, 1) - }, - ); err != nil { - logrus.WithError(err).Error("unable to register gotrue.running gague metric") - } }) return err diff --git a/internal/observability/request-tracing.go b/internal/observability/request-tracing.go index aefa994db..e8ee61bc1 100644 --- a/internal/observability/request-tracing.go +++ b/internal/observability/request-tracing.go @@ -1,16 +1,15 @@ package observability import ( - "context" "net/http" "github.com/go-chi/chi/v5" "github.com/sirupsen/logrus" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" + "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" - metricglobal "go.opentelemetry.io/otel/metric/global" - metricinstrument "go.opentelemetry.io/otel/metric/instrument" - semconv "go.opentelemetry.io/otel/semconv/v1.12.0" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.25.0" "go.opentelemetry.io/otel/trace" ) @@ -80,14 +79,10 @@ func (w *interceptingResponseWriter) Header() http.Header { return w.writer.Header() } -type metricCounter interface { - Add(ctx context.Context, incr int64, attrs ...attribute.KeyValue) -} - // countStatusCodesSafely counts the number of HTTP status codes per route that // occurred while GoTrue was running. If it is not able to identify the route // via chi.RouteContext(ctx).RoutePattern() it counts with a noroute attribute. -func countStatusCodesSafely(w *interceptingResponseWriter, r *http.Request, counter metricCounter) { +func countStatusCodesSafely(w *interceptingResponseWriter, r *http.Request, counter metric.Int64Counter) { if counter == nil { return } @@ -95,12 +90,12 @@ func countStatusCodesSafely(w *interceptingResponseWriter, r *http.Request, coun defer func() { if rec := recover(); rec != nil { logrus.WithField("error", rec).Error("unable to count status codes safely, metrics may be off") - counter.Add( r.Context(), 1, - attribute.Bool("noroute", true), - attribute.Int("code", w.statusCode), + metric.WithAttributes( + attribute.Bool("noroute", true), + attribute.Int("code", w.statusCode)), ) } }() @@ -113,8 +108,7 @@ func countStatusCodesSafely(w *interceptingResponseWriter, r *http.Request, coun counter.Add( ctx, 1, - attribute.Int("code", w.statusCode), - routePattern, + metric.WithAttributes(attribute.Int("code", w.statusCode), routePattern), ) } @@ -122,10 +116,10 @@ func countStatusCodesSafely(w *interceptingResponseWriter, r *http.Request, coun // in. Supports Chi routers, so this should be one of the first middlewares on // the router. func RequestTracing() func(http.Handler) http.Handler { - meter := metricglobal.Meter("gotrue") - statusCodes, err := meter.SyncInt64().Counter( + meter := otel.Meter("gotrue") + statusCodes, err := meter.Int64Counter( "http_status_codes", - metricinstrument.WithDescription("Number of returned HTTP status codes"), + metric.WithDescription("Number of returned HTTP status codes"), ) if err != nil { logrus.WithError(err).Error("unable to get gotrue.http_status_codes counter metric") From 357bda23cb2abd12748df80a9d27288aa548534d Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 12 Jun 2024 17:52:32 +0800 Subject: [PATCH 05/42] fix: define search path in auth functions (#1616) ## What kind of change does this PR introduce? * Set search_path to empty string in all auth functions --- .../20240612114525_set_search_path.up.sql | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 migrations/20240612114525_set_search_path.up.sql diff --git a/migrations/20240612114525_set_search_path.up.sql b/migrations/20240612114525_set_search_path.up.sql new file mode 100644 index 000000000..5d6ff2081 --- /dev/null +++ b/migrations/20240612114525_set_search_path.up.sql @@ -0,0 +1,43 @@ +-- set the search_path to an empty string to force fully qualified names in the function +do $$ +begin + -- auth.uid() function + create or replace function auth.uid() + returns uuid + set search_path to '' + as $func$ + select nullif(current_setting('request.jwt.claim.sub', true), '')::uuid; + $func$ language sql stable; + + -- auth.role() function + create or replace function {{ index .Options "Namespace" }}.role() + returns text + set search_path to '' + as $func$ + select nullif(current_setting('request.jwt.claim.role', true), '')::text; + $func$ language sql stable; + + -- auth.email() function + create or replace function {{ index .Options "Namespace" }}.email() + returns text + set search_path to '' + as $func$ + select + coalesce( + current_setting('request.jwt.claim.email', true), + (current_setting('request.jwt.claims', true)::jsonb ->> 'email') + )::text + $func$ language sql stable; + + -- auth.jwt() function + create or replace function {{ index .Options "Namespace" }}.jwt() + returns jsonb + set search_path to '' + as $func$ + select + coalesce( + nullif(current_setting('request.jwt.claim', true), ''), + nullif(current_setting('request.jwt.claims', true), '') + )::jsonb; + $func$ language sql stable; +end $$; From e4a475820b2dc1f985bd37df15a8ab9e781626f5 Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Wed, 12 Jun 2024 13:33:43 +0200 Subject: [PATCH 06/42] feat: encrypt sensitive columns (#1593) Adds support for encrypting sensitive columns like the MFA secret and password hash. The goal with this encryption mechanism is to add yet another layer of security on top of the database permissions provided by Postgres. In the event that the database leaks or is accessed by malicious users or the database permissions are incorrectly defined, the encryption key would also be required to inspect this sensitive data. Encryption is done using AES-GCM-256. Strings that are encrypted are converted into a JSON string with this shape: ```json { "key_id": "key identifier used for encryption", "alg": "aes-gcm-hkdf", "nonce": "GCM 12 byte nonce", "data": "Base64 standard encoding of the ciphertext" } ``` As AES-GCM must not be used more than 2^32 times with a single symmetric key, and this is not that much -- imagine serving 100m users -- then this means that all users can only add 42 passwords or MFA verification factors before running into this hard limit. To fix this, a symmetric key is derived using [HKDF](https://datatracker.ietf.org/doc/html/rfc5869) with SHA256 such that the symmetric key is used together with the object ID (for passwords - the user ID, for TOTP secrets - the factor ID). This way there's a separate AES-GCM key per object, and additionally gives the security property that a malicious actor with write permissions to the database cannot swap passwords / TOTP secrets from Malice's account to Target's account. They would need to also change the UUIDs of these objects, which is likely to be hard. To turn on encryption the following configs need to be added: `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true` -- that turns on encryption for new objects. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=key-id` -- ID of the encryption key, allowing to rotate keys easily. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=key` -- Base64 URL encoding of a 256 bit AES key Once encryption has been turned on, in order to have the rows be readable **for ever** this config must be provided with all past and future keys: `GOTRUE_SECURITY_DB_ENCRYPTION_DECRYPTION_KEYS=key-id:key` -- A map of key IDs and Base64 URL key encodings of the keys. To retire keys, you should just move the old key to the decryption keys map, and advertise the new encryption key ID. On each successful sign in with password, or any MFA verification attempt, the latest key will be used to re-encrypt the column. This also applies for the non-encrypted-to-encrypted case. --- hack/test.env | 4 + internal/api/admin.go | 3 +- internal/api/admin_test.go | 14 +++- internal/api/mfa.go | 57 +++++++++++--- internal/api/mfa_test.go | 23 +++--- internal/api/token.go | 19 ++++- internal/api/user.go | 15 +++- internal/api/user_test.go | 15 +++- internal/api/verify.go | 4 +- internal/conf/configuration.go | 61 ++++++++++++++- internal/crypto/crypto.go | 138 +++++++++++++++++++++++++++++++++ internal/crypto/crypto_test.go | 34 ++++++++ internal/models/factor.go | 33 +++++++- internal/models/factor_test.go | 3 +- internal/models/user.go | 29 ++++++- internal/models/user_test.go | 4 +- 16 files changed, 413 insertions(+), 43 deletions(-) create mode 100644 internal/crypto/crypto_test.go diff --git a/hack/test.env b/hack/test.env index f4f3d0e6e..409940314 100644 --- a/hack/test.env +++ b/hack/test.env @@ -114,3 +114,7 @@ GOTRUE_SAML_ENABLED="true" GOTRUE_SAML_PRIVATE_KEY="MIIEowIBAAKCAQEAszrVveMQcSsa0Y+zN1ZFb19cRS0jn4UgIHTprW2tVBmO2PABzjY3XFCfx6vPirMAPWBYpsKmXrvm1tr0A6DZYmA8YmJd937VUQ67fa6DMyppBYTjNgGEkEhmKuszvF3MARsIKCGtZqUrmS7UG4404wYxVppnr2EYm3RGtHlkYsXu20MBqSDXP47bQP+PkJqC3BuNGk3xt5UHl2FSFpTHelkI6lBynw16B+lUT1F96SERNDaMqi/TRsZdGe5mB/29ngC/QBMpEbRBLNRir5iUevKS7Pn4aph9Qjaxx/97siktK210FJT23KjHpgcUfjoQ6BgPBTLtEeQdRyDuc/CgfwIDAQABAoIBAGYDWOEpupQPSsZ4mjMnAYJwrp4ZISuMpEqVAORbhspVeb70bLKonT4IDcmiexCg7cQBcLQKGpPVM4CbQ0RFazXZPMVq470ZDeWDEyhoCfk3bGtdxc1Zc9CDxNMs6FeQs6r1beEZug6weG5J/yRn/qYxQife3qEuDMl+lzfl2EN3HYVOSnBmdt50dxRuX26iW3nqqbMRqYn9OHuJ1LvRRfYeyVKqgC5vgt/6Tf7DAJwGe0dD7q08byHV8DBZ0pnMVU0bYpf1GTgMibgjnLjK//EVWafFHtN+RXcjzGmyJrk3+7ZyPUpzpDjO21kpzUQLrpEkkBRnmg6bwHnSrBr8avECgYEA3pq1PTCAOuLQoIm1CWR9/dhkbJQiKTJevlWV8slXQLR50P0WvI2RdFuSxlWmA4xZej8s4e7iD3MYye6SBsQHygOVGc4efvvEZV8/XTlDdyj7iLVGhnEmu2r7AFKzy8cOvXx0QcLg+zNd7vxZv/8D3Qj9Jje2LjLHKM5n/dZ3RzUCgYEAzh5Lo2anc4WN8faLGt7rPkGQF+7/18ImQE11joHWa3LzAEy7FbeOGpE/vhOv5umq5M/KlWFIRahMEQv4RusieHWI19ZLIP+JwQFxWxS+cPp3xOiGcquSAZnlyVSxZ//dlVgaZq2o2MfrxECcovRlaknl2csyf+HjFFwKlNxHm2MCgYAr//R3BdEy0oZeVRndo2lr9YvUEmu2LOihQpWDCd0fQw0ZDA2kc28eysL2RROte95r1XTvq6IvX5a0w11FzRWlDpQ4J4/LlcQ6LVt+98SoFwew+/PWuyLmxLycUbyMOOpm9eSc4wJJZNvaUzMCSkvfMtmm5jgyZYMMQ9A2Ul/9SQKBgB9mfh9mhBwVPIqgBJETZMMXOdxrjI5SBYHGSyJqpT+5Q0vIZLfqPrvNZOiQFzwWXPJ+tV4Mc/YorW3rZOdo6tdvEGnRO6DLTTEaByrY/io3/gcBZXoSqSuVRmxleqFdWWRnB56c1hwwWLqNHU+1671FhL6pNghFYVK4suP6qu4BAoGBAMk+VipXcIlD67mfGrET/xDqiWWBZtgTzTMjTpODhDY1GZck1eb4CQMP5j5V3gFJ4cSgWDJvnWg8rcz0unz/q4aeMGl1rah5WNDWj1QKWMS6vJhMHM/rqN1WHWR0ZnV83svYgtg0zDnQKlLujqW4JmGXLMU7ur6a+e6lpa1fvLsP" GOTRUE_MAX_VERIFIED_FACTORS=10 GOTRUE_SMS_TEST_OTP_VALID_UNTIL="" +GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true +GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=abc +GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=pwFoiPyybQMqNmYVN0gUnpbfpGQV2sDv9vp0ZAxi_Y4 +GOTRUE_SECURITY_DB_ENCRYPTION_DECRYPTION_KEYS=abc:pwFoiPyybQMqNmYVN0gUnpbfpGQV2sDv9vp0ZAxi_Y4 diff --git a/internal/api/admin.go b/internal/api/admin.go index dfb5bf8fa..053a75d35 100644 --- a/internal/api/admin.go +++ b/internal/api/admin.go @@ -134,6 +134,7 @@ func (a *API) adminUserGet(w http.ResponseWriter, r *http.Request) error { func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { ctx := r.Context() db := a.db.WithContext(ctx) + config := a.config user := getUser(ctx) adminUser := getAdminUser(ctx) params, err := a.getAdminParams(r) @@ -175,7 +176,7 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { return err } - if err := user.SetPassword(ctx, password); err != nil { + if err := user.SetPassword(ctx, password, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { return err } } diff --git a/internal/api/admin_test.go b/internal/api/admin_test.go index c57659414..fa046045e 100644 --- a/internal/api/admin_test.go +++ b/internal/api/admin_test.go @@ -350,7 +350,10 @@ func (ts *AdminTestSuite) TestAdminUserCreate() { expectedPassword = fmt.Sprintf("%v", c.params["password"]) } - assert.Equal(ts.T(), c.expected["isAuthenticated"], u.Authenticate(context.Background(), expectedPassword)) + isAuthenticated, _, err := u.Authenticate(context.Background(), expectedPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) + require.NoError(ts.T(), err) + + assert.Equal(ts.T(), c.expected["isAuthenticated"], isAuthenticated) // remove created user after each case require.NoError(ts.T(), ts.API.db.Destroy(u)) @@ -726,7 +729,8 @@ func (ts *AdminTestSuite) TestAdminUserDeleteFactor() { require.NoError(ts.T(), err, "Error making new user") require.NoError(ts.T(), ts.API.db.Create(u), "Error creating user") - f := models.NewFactor(u, "testSimpleName", models.TOTP, models.FactorStateVerified, "secretkey") + f := models.NewFactor(u, "testSimpleName", models.TOTP, models.FactorStateVerified) + require.NoError(ts.T(), f.SetSecret("secretkey", ts.Config.Security.DBEncryption.Encrypt, ts.Config.Security.DBEncryption.EncryptionKeyID, ts.Config.Security.DBEncryption.EncryptionKey)) require.NoError(ts.T(), ts.API.db.Create(f), "Error saving new test factor") // Setup request @@ -749,7 +753,8 @@ func (ts *AdminTestSuite) TestAdminUserGetFactors() { require.NoError(ts.T(), err, "Error making new user") require.NoError(ts.T(), ts.API.db.Create(u), "Error creating user") - f := models.NewFactor(u, "testSimpleName", models.TOTP, models.FactorStateUnverified, "secretkey") + f := models.NewFactor(u, "testSimpleName", models.TOTP, models.FactorStateUnverified) + require.NoError(ts.T(), f.SetSecret("secretkey", ts.Config.Security.DBEncryption.Encrypt, ts.Config.Security.DBEncryption.EncryptionKeyID, ts.Config.Security.DBEncryption.EncryptionKey)) require.NoError(ts.T(), ts.API.db.Create(f), "Error saving new test factor") // Setup request @@ -770,7 +775,8 @@ func (ts *AdminTestSuite) TestAdminUserUpdateFactor() { require.NoError(ts.T(), err, "Error making new user") require.NoError(ts.T(), ts.API.db.Create(u), "Error creating user") - f := models.NewFactor(u, "testSimpleName", models.TOTP, models.FactorStateUnverified, "secretkey") + f := models.NewFactor(u, "testSimpleName", models.TOTP, models.FactorStateUnverified) + require.NoError(ts.T(), f.SetSecret("secretkey", ts.Config.Security.DBEncryption.Encrypt, ts.Config.Security.DBEncryption.EncryptionKeyID, ts.Config.Security.DBEncryption.EncryptionKey)) require.NoError(ts.T(), ts.API.db.Create(f), "Error saving new test factor") var cases = []struct { diff --git a/internal/api/mfa.go b/internal/api/mfa.go index 07221fc2e..d2e8295f7 100644 --- a/internal/api/mfa.go +++ b/internal/api/mfa.go @@ -11,6 +11,7 @@ import ( "github.com/boombuler/barcode/qr" "github.com/gofrs/uuid" "github.com/pquerna/otp/totp" + "github.com/supabase/auth/internal/crypto" "github.com/supabase/auth/internal/hooks" "github.com/supabase/auth/internal/metering" "github.com/supabase/auth/internal/models" @@ -63,6 +64,7 @@ func (a *API) EnrollFactor(w http.ResponseWriter, r *http.Request) error { user := getUser(ctx) session := getSession(ctx) config := a.config + db := a.db.WithContext(ctx) if session == nil || user == nil { return internalServerError("A valid session and a registered user are required to enroll a factor") @@ -92,7 +94,7 @@ func (a *API) EnrollFactor(w http.ResponseWriter, r *http.Request) error { factorCount := len(factors) numVerifiedFactors := 0 - if err := models.DeleteExpiredFactors(a.db, config.MFA.FactorExpiryDuration); err != nil { + if err := models.DeleteExpiredFactors(db, config.MFA.FactorExpiryDuration); err != nil { return err } @@ -132,9 +134,12 @@ func (a *API) EnrollFactor(w http.ResponseWriter, r *http.Request) error { } svgData.End() - factor := models.NewFactor(user, params.FriendlyName, params.FactorType, models.FactorStateUnverified, key.Secret()) + factor := models.NewFactor(user, params.FriendlyName, params.FactorType, models.FactorStateUnverified) + if err := factor.SetSecret(key.Secret(), config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { + return err + } - err = a.db.Transaction(func(tx *storage.Connection) error { + err = db.Transaction(func(tx *storage.Connection) error { if terr := tx.Create(factor); terr != nil { pgErr := utilities.NewPostgresError(terr) if pgErr.IsUniqueConstraintViolated() { @@ -161,7 +166,7 @@ func (a *API) EnrollFactor(w http.ResponseWriter, r *http.Request) error { TOTP: TOTPObject{ // See: https://css-tricks.com/probably-dont-base64-svg/ QRCode: buf.String(), - Secret: factor.Secret, + Secret: key.Secret(), URI: key.URL(), }, }) @@ -170,13 +175,14 @@ func (a *API) EnrollFactor(w http.ResponseWriter, r *http.Request) error { func (a *API) ChallengeFactor(w http.ResponseWriter, r *http.Request) error { ctx := r.Context() config := a.config + db := a.db.WithContext(ctx) user := getUser(ctx) factor := getFactor(ctx) ipAddress := utilities.GetIPAddress(r) challenge := models.NewChallenge(factor, ipAddress) - if err := a.db.Transaction(func(tx *storage.Connection) error { + if err := db.Transaction(func(tx *storage.Connection) error { if terr := tx.Create(challenge); terr != nil { return terr } @@ -203,6 +209,7 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { user := getUser(ctx) factor := getFactor(ctx) config := a.config + db := a.db.WithContext(ctx) params := &VerifyFactorParams{} if err := retrieveRequestParams(r, params); err != nil { @@ -214,7 +221,7 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { return internalServerError(InvalidFactorOwnerErrorMessage) } - challenge, err := models.FindChallengeByID(a.db, params.ChallengeID) + challenge, err := models.FindChallengeByID(db, params.ChallengeID) if err != nil && models.IsNotFoundError(err) { return notFoundError(ErrorCodeMFAFactorNotFound, "MFA factor with the provided challenge ID not found") } else if err != nil { @@ -226,13 +233,18 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { } if challenge.HasExpired(config.MFA.ChallengeExpiryDuration) { - if err := a.db.Destroy(challenge); err != nil { + if err := db.Destroy(challenge); err != nil { return internalServerError("Database error deleting challenge").WithInternalError(err) } return unprocessableEntityError(ErrorCodeMFAChallengeExpired, "MFA challenge %v has expired, verify against another challenge or create a new challenge.", challenge.ID) } - valid := totp.Validate(params.Code, factor.Secret) + secret, shouldReEncrypt, err := factor.GetSecret(config.Security.DBEncryption.DecryptionKeys, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID) + if err != nil { + return internalServerError("Database error verifying MFA TOTP secret").WithInternalError(err) + } + + valid := totp.Validate(params.Code, secret) if config.Hook.MFAVerificationAttempt.Enabled { input := hooks.MFAVerificationAttemptInput{ @@ -248,7 +260,7 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { } if output.Decision == hooks.HookRejection { - if err := models.Logout(a.db, user.ID); err != nil { + if err := models.Logout(db, user.ID); err != nil { return err } @@ -259,12 +271,22 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { return forbiddenError(ErrorCodeMFAVerificationRejected, output.Message) } } + if !valid { + if shouldReEncrypt && config.Security.DBEncryption.Encrypt { + if err := factor.SetSecret(secret, true, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { + return err + } + + if err := db.UpdateOnly(factor, "secret"); err != nil { + return err + } + } return unprocessableEntityError(ErrorCodeMFAVerificationFailed, "Invalid TOTP code entered") } var token *AccessTokenResponse - err = a.db.Transaction(func(tx *storage.Connection) error { + err = db.Transaction(func(tx *storage.Connection) error { var terr error if terr = models.NewAuditLogEntry(r, tx, user, models.VerifyFactorAction, r.RemoteAddr, map[string]interface{}{ "factor_id": factor.ID, @@ -280,6 +302,17 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { return terr } } + if shouldReEncrypt && config.Security.DBEncryption.Encrypt { + es, terr := crypto.NewEncryptedString(factor.ID.String(), []byte(secret), config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey) + if terr != nil { + return terr + } + + factor.Secret = es.String() + if terr := tx.UpdateOnly(factor, "secret"); terr != nil { + return terr + } + } user, terr = models.FindUserByID(tx, user.ID) if terr != nil { return terr @@ -316,6 +349,8 @@ func (a *API) UnenrollFactor(w http.ResponseWriter, r *http.Request) error { user := getUser(ctx) factor := getFactor(ctx) session := getSession(ctx) + db := a.db.WithContext(ctx) + if factor == nil || session == nil || user == nil { return internalServerError("A valid session and factor are required to unenroll a factor") } @@ -327,7 +362,7 @@ func (a *API) UnenrollFactor(w http.ResponseWriter, r *http.Request) error { return internalServerError(InvalidFactorOwnerErrorMessage) } - err = a.db.Transaction(func(tx *storage.Connection) error { + err = db.Transaction(func(tx *storage.Connection) error { var terr error if terr := tx.Destroy(factor); terr != nil { return terr diff --git a/internal/api/mfa_test.go b/internal/api/mfa_test.go index 991cc52f9..63f813249 100644 --- a/internal/api/mfa_test.go +++ b/internal/api/mfa_test.go @@ -2,7 +2,6 @@ package api import ( "bytes" - "context" "encoding/json" "fmt" "net/http" @@ -14,15 +13,15 @@ import ( "github.com/gofrs/uuid" "database/sql" + "github.com/pkg/errors" "github.com/pquerna/otp" "github.com/supabase/auth/internal/conf" + "github.com/supabase/auth/internal/crypto" "github.com/supabase/auth/internal/models" "github.com/supabase/auth/internal/storage" "github.com/supabase/auth/internal/utilities" - "github.com/jackc/pgx/v4" - "github.com/pquerna/otp/totp" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -62,7 +61,8 @@ func (ts *MFATestSuite) SetupTest() { require.NoError(ts.T(), err, "Error creating test user model") require.NoError(ts.T(), ts.API.db.Create(u), "Error saving new test user") // Create Factor - f := models.NewFactor(u, "test_factor", models.TOTP, models.FactorStateUnverified, "secretkey") + f := models.NewFactor(u, "test_factor", models.TOTP, models.FactorStateUnverified) + require.NoError(ts.T(), f.SetSecret("secretkey", ts.Config.Security.DBEncryption.Encrypt, ts.Config.Security.DBEncryption.EncryptionKeyID, ts.Config.Security.DBEncryption.EncryptionKey)) require.NoError(ts.T(), ts.API.db.Create(f), "Error saving new test factor") // Create corresponding session s, err := models.NewSession(u.ID, &f.ID) @@ -482,14 +482,19 @@ func ServeAuthenticatedRequest(ts *MFATestSuite, method, path, token string, buf func performVerifyFlow(ts *MFATestSuite, challengeID, factorID uuid.UUID, token string, requireStatusOK bool) *httptest.ResponseRecorder { var buffer bytes.Buffer - conn, err := pgx.Connect(context.Background(), ts.API.db.URL()) + factor, err := models.FindFactorByFactorID(ts.API.db, factorID) require.NoError(ts.T(), err) + require.NotNil(ts.T(), factor) - defer conn.Close(context.Background()) + totpSecret := factor.Secret - var totpSecret string - err = conn.QueryRow(context.Background(), "select secret from mfa_factors where id=$1", factorID).Scan(&totpSecret) - require.NoError(ts.T(), err) + if es := crypto.ParseEncryptedString(factor.Secret); es != nil { + secret, err := es.Decrypt(factor.ID.String(), ts.API.config.Security.DBEncryption.DecryptionKeys) + require.NoError(ts.T(), err) + require.NotNil(ts.T(), secret) + + totpSecret = string(secret) + } code, err := totp.GenerateCode(totpSecret, time.Now().UTC()) require.NoError(ts.T(), err) diff --git a/internal/api/token.go b/internal/api/token.go index 542c68edf..11af2883f 100644 --- a/internal/api/token.go +++ b/internal/api/token.go @@ -145,7 +145,10 @@ func (a *API) ResourceOwnerPasswordGrant(ctx context.Context, w http.ResponseWri return oauthError("invalid_grant", InvalidLoginMessage) } - isValidPassword := user.Authenticate(ctx, params.Password) + isValidPassword, shouldReEncrypt, err := user.Authenticate(ctx, params.Password, config.Security.DBEncryption.DecryptionKeys, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID) + if err != nil { + return err + } var weakPasswordError *WeakPasswordError if isValidPassword { @@ -156,6 +159,20 @@ func (a *API) ResourceOwnerPasswordGrant(ctx context.Context, w http.ResponseWri observability.GetLogEntry(r).Entry.WithError(err).Warn("Password strength check on sign-in failed") } } + + if shouldReEncrypt { + if err := user.SetPassword(ctx, params.Password, true, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { + return err + } + + // directly change this in the database without + // calling user.UpdatePassword() because this + // is not a password change, just encryption + // change in the database + if err := db.UpdateOnly(user, "encrypted_password"); err != nil { + return err + } + } } if config.Hook.PasswordVerificationAttempt.Enabled { diff --git a/internal/api/user.go b/internal/api/user.go index 10fbc93d2..33c35aa57 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -153,12 +153,23 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { password := *params.Password if password != "" { - if user.EncryptedPassword != "" && user.Authenticate(ctx, password) { + isSamePassword := false + + if user.EncryptedPassword != "" { + auth, _, err := user.Authenticate(ctx, password, config.Security.DBEncryption.DecryptionKeys, false, "") + if err != nil { + return err + } + + isSamePassword = auth + } + + if isSamePassword { return unprocessableEntityError(ErrorCodeSamePassword, "New password should be different from the old password.") } } - if err := user.SetPassword(ctx, password); err != nil { + if err := user.SetPassword(ctx, password, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { return err } } diff --git a/internal/api/user_test.go b/internal/api/user_test.go index ac97d9c24..8272bb87e 100644 --- a/internal/api/user_test.go +++ b/internal/api/user_test.go @@ -310,7 +310,10 @@ func (ts *UserTestSuite) TestUserUpdatePassword() { u, err = models.FindUserByEmailAndAudience(ts.API.db, "test@example.com", ts.Config.JWT.Aud) require.NoError(ts.T(), err) - require.Equal(ts.T(), c.expected.isAuthenticated, u.Authenticate(context.Background(), c.newPassword)) + isAuthenticated, _, err := u.Authenticate(context.Background(), c.newPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) + require.NoError(ts.T(), err) + + require.Equal(ts.T(), c.expected.isAuthenticated, isAuthenticated) }) } } @@ -369,7 +372,10 @@ func (ts *UserTestSuite) TestUserUpdatePasswordNoReauthenticationRequired() { u, err = models.FindUserByEmailAndAudience(ts.API.db, "test@example.com", ts.Config.JWT.Aud) require.NoError(ts.T(), err) - require.Equal(ts.T(), c.expected.isAuthenticated, u.Authenticate(context.Background(), c.newPassword)) + isAuthenticated, _, err := u.Authenticate(context.Background(), c.newPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) + require.NoError(ts.T(), err) + + require.Equal(ts.T(), c.expected.isAuthenticated, isAuthenticated) }) } } @@ -424,7 +430,10 @@ func (ts *UserTestSuite) TestUserUpdatePasswordReauthentication() { u, err = models.FindUserByEmailAndAudience(ts.API.db, "test@example.com", ts.Config.JWT.Aud) require.NoError(ts.T(), err) - require.True(ts.T(), u.Authenticate(context.Background(), "newpass")) + isAuthenticated, _, err := u.Authenticate(context.Background(), "newpass", ts.Config.Security.DBEncryption.DecryptionKeys, ts.Config.Security.DBEncryption.Encrypt, ts.Config.Security.DBEncryption.EncryptionKeyID) + require.NoError(ts.T(), err) + + require.True(ts.T(), isAuthenticated) require.Empty(ts.T(), u.ReauthenticationToken) require.Nil(ts.T(), u.ReauthenticationSentAt) } diff --git a/internal/api/verify.go b/internal/api/verify.go index 5badfc77e..91df8bc21 100644 --- a/internal/api/verify.go +++ b/internal/api/verify.go @@ -304,6 +304,8 @@ func (a *API) verifyPost(w http.ResponseWriter, r *http.Request, params *VerifyP } func (a *API) signupVerify(r *http.Request, ctx context.Context, conn *storage.Connection, user *models.User) (*models.User, error) { + config := a.config + if user.EncryptedPassword == "" && user.InvitedAt != nil { // sign them up with temporary password, and require application // to present the user with a password set form @@ -313,7 +315,7 @@ func (a *API) signupVerify(r *http.Request, ctx context.Context, conn *storage.C panic(err) } - if err := user.SetPassword(ctx, password); err != nil { + if err := user.SetPassword(ctx, password, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { return nil, err } } diff --git a/internal/conf/configuration.go b/internal/conf/configuration.go index 2c8212abd..99f0f1879 100644 --- a/internal/conf/configuration.go +++ b/internal/conf/configuration.go @@ -2,6 +2,7 @@ package conf import ( "bytes" + "encoding/base64" "errors" "fmt" "net/url" @@ -421,16 +422,74 @@ func (c *CaptchaConfiguration) Validate() error { return nil } +// DatabaseEncryptionConfiguration configures Auth to encrypt certain columns. +// Once Encrypt is set to true, data will start getting encrypted with the +// provided encryption key. Setting it to false just stops encryption from +// going on further, but DecryptionKeys would have to contain the same key so +// the encrypted data remains accessible. +type DatabaseEncryptionConfiguration struct { + Encrypt bool `json:"encrypt"` + + EncryptionKeyID string `json:"encryption_key_id" split_words:"true"` + EncryptionKey string `json:"-" split_words:"true"` + + DecryptionKeys map[string]string `json:"-" split_words:"true"` +} + +func (c *DatabaseEncryptionConfiguration) Validate() error { + if c.Encrypt { + if c.EncryptionKeyID == "" { + return errors.New("conf: encryption key ID must be specified") + } + + decodedKey, err := base64.RawURLEncoding.DecodeString(c.EncryptionKey) + if err != nil { + return err + } + + if len(decodedKey) != 256/8 { + return errors.New("conf: encryption key is not 256 bits") + } + + if c.DecryptionKeys == nil || c.DecryptionKeys[c.EncryptionKeyID] == "" { + return errors.New("conf: encryption key must also be present in decryption keys") + } + } + + for id, key := range c.DecryptionKeys { + decodedKey, err := base64.RawURLEncoding.DecodeString(key) + if err != nil { + return err + } + + if len(decodedKey) != 256/8 { + return fmt.Errorf("conf: decryption key with ID %q must be 256 bits", id) + } + } + + return nil +} + type SecurityConfiguration struct { Captcha CaptchaConfiguration `json:"captcha"` RefreshTokenRotationEnabled bool `json:"refresh_token_rotation_enabled" split_words:"true" default:"true"` RefreshTokenReuseInterval int `json:"refresh_token_reuse_interval" split_words:"true"` UpdatePasswordRequireReauthentication bool `json:"update_password_require_reauthentication" split_words:"true"` ManualLinkingEnabled bool `json:"manual_linking_enabled" split_words:"true" default:"false"` + + DBEncryption DatabaseEncryptionConfiguration `json:"database_encryption" split_words:"true"` } func (c *SecurityConfiguration) Validate() error { - return c.Captcha.Validate() + if err := c.Captcha.Validate(); err != nil { + return err + } + + if err := c.DBEncryption.Validate(); err != nil { + return err + } + + return nil } func loadEnvironment(filename string) error { diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go index 590d1ba4d..be6a2b5df 100644 --- a/internal/crypto/crypto.go +++ b/internal/crypto/crypto.go @@ -1,9 +1,12 @@ package crypto import ( + "crypto/aes" + "crypto/cipher" "crypto/rand" "crypto/sha256" "encoding/base64" + "encoding/json" "fmt" "io" "math" @@ -14,6 +17,7 @@ import ( "github.com/gofrs/uuid" standardwebhooks "github.com/standard-webhooks/standard-webhooks/libraries/go" + "golang.org/x/crypto/hkdf" "github.com/pkg/errors" ) @@ -69,3 +73,137 @@ func GenerateSignatures(secrets []string, msgID uuid.UUID, currentTime time.Time } return signatureList, nil } + +type EncryptedString struct { + KeyID string `json:"key_id"` + Algorithm string `json:"alg"` + Data []byte `json:"data"` + Nonce []byte `json:"nonce,omitempty"` +} + +func (es *EncryptedString) IsValid() bool { + return es.KeyID != "" && len(es.Data) > 0 && len(es.Nonce) > 0 && es.Algorithm == "aes-gcm-hkdf" +} + +// ShouldReEncrypt tells you if the value encrypted needs to be encrypted again with a newer key. +func (es *EncryptedString) ShouldReEncrypt(encryptionKeyID string) bool { + return es.KeyID != encryptionKeyID +} + +func (es *EncryptedString) Decrypt(id string, decryptionKeys map[string]string) ([]byte, error) { + decryptionKey := decryptionKeys[es.KeyID] + + if decryptionKey == "" { + return nil, fmt.Errorf("crypto: decryption key with name %q does not exist", es.KeyID) + } + + key, err := deriveSymmetricKey(id, es.KeyID, decryptionKey) + if err != nil { + return nil, err + } + + aes, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + cipher, err := cipher.NewGCM(aes) + if err != nil { + return nil, err + } + + decrypted, err := cipher.Open(nil, es.Nonce, es.Data, nil) + if err != nil { + return nil, err + } + + return decrypted, nil +} + +func ParseEncryptedString(str string) *EncryptedString { + if !strings.HasPrefix(str, "{") { + return nil + } + + var es EncryptedString + + if err := json.Unmarshal([]byte(str), &es); err != nil { + return nil + } + + if !es.IsValid() { + return nil + } + + return &es +} + +func (es *EncryptedString) String() string { + out, err := json.Marshal(es) + if err != nil { + panic(err) + } + + return string(out) +} + +func deriveSymmetricKey(id, keyID, keyBase64URL string) ([]byte, error) { + hkdfKey, err := base64.RawURLEncoding.DecodeString(keyBase64URL) + if err != nil { + return nil, err + } + + if len(hkdfKey) != 256/8 { + return nil, fmt.Errorf("crypto: key with ID %q is not 256 bits", keyID) + } + + // Since we use AES-GCM here, the same symmetric key *must not be used + // more than* 2^32 times. But, that's not that much. Suppose a system + // with 100 million users, then a user can only change their password + // 42 times. To prevent this, the actual symmetric key is derived by + // using HKDF using the encryption key and the "ID" of the object + // containing the encryption string. Ideally this ID is a UUID. This + // has the added benefit that the encrypted string is bound to that + // specific object, and can't accidentally be "moved" to other objects + // without changing their ID to the original one. + + keyReader := hkdf.New(sha256.New, hkdfKey, nil, []byte(id)) + key := make([]byte, 256/8) + + if _, err := io.ReadFull(keyReader, key); err != nil { + panic(err) + } + + return key, nil +} + +func NewEncryptedString(id string, data []byte, keyID string, keyBase64URL string) (*EncryptedString, error) { + key, err := deriveSymmetricKey(id, keyID, keyBase64URL) + if err != nil { + return nil, err + } + + aes, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + cipher, err := cipher.NewGCM(aes) + if err != nil { + panic(err) + } + + es := EncryptedString{ + KeyID: keyID, + Algorithm: "aes-gcm-hkdf", + Nonce: make([]byte, 12), + } + + if _, err := io.ReadFull(rand.Reader, es.Nonce); err != nil { + panic(err) + } + + es.Data = cipher.Seal(nil, es.Nonce, data, nil) + + return &es, nil +} diff --git a/internal/crypto/crypto_test.go b/internal/crypto/crypto_test.go new file mode 100644 index 000000000..b677b918d --- /dev/null +++ b/internal/crypto/crypto_test.go @@ -0,0 +1,34 @@ +package crypto + +import ( + "testing" + + "github.com/gofrs/uuid" + "github.com/stretchr/testify/assert" +) + +func TestEncryptedString(t *testing.T) { + id := uuid.Must(uuid.NewV4()).String() + + es, err := NewEncryptedString(id, []byte("data"), "key-id", "pwFoiPyybQMqNmYVN0gUnpbfpGQV2sDv9vp0ZAxi_Y4") + assert.NoError(t, err) + + assert.Equal(t, es.KeyID, "key-id") + assert.Equal(t, es.Algorithm, "aes-gcm-hkdf") + assert.Len(t, es.Data, 20) + assert.Len(t, es.Nonce, 12) + + dec := ParseEncryptedString(es.String()) + + assert.NotNil(t, dec) + assert.Equal(t, dec.Algorithm, "aes-gcm-hkdf") + assert.Len(t, dec.Data, 20) + assert.Len(t, dec.Nonce, 12) + + decrypted, err := dec.Decrypt(id, map[string]string{ + "key-id": "pwFoiPyybQMqNmYVN0gUnpbfpGQV2sDv9vp0ZAxi_Y4", + }) + + assert.NoError(t, err) + assert.Equal(t, []byte("data"), decrypted) +} diff --git a/internal/models/factor.go b/internal/models/factor.go index 265733564..53fddc260 100644 --- a/internal/models/factor.go +++ b/internal/models/factor.go @@ -9,6 +9,7 @@ import ( "github.com/gobuffalo/pop/v6" "github.com/gofrs/uuid" "github.com/pkg/errors" + "github.com/supabase/auth/internal/crypto" "github.com/supabase/auth/internal/storage" ) @@ -127,20 +128,46 @@ func (Factor) TableName() string { return tableName } -func NewFactor(user *User, friendlyName string, factorType string, state FactorState, secret string) *Factor { +func NewFactor(user *User, friendlyName string, factorType string, state FactorState) *Factor { id := uuid.Must(uuid.NewV4()) factor := &Factor{ - UserID: user.ID, ID: id, + UserID: user.ID, Status: state.String(), FriendlyName: friendlyName, - Secret: secret, FactorType: factorType, } return factor } +func (f *Factor) SetSecret(secret string, encrypt bool, encryptionKeyID, encryptionKey string) error { + f.Secret = secret + if encrypt { + es, err := crypto.NewEncryptedString(f.ID.String(), []byte(secret), encryptionKeyID, encryptionKey) + if err != nil { + return err + } + + f.Secret = es.String() + } + + return nil +} + +func (f *Factor) GetSecret(decryptionKeys map[string]string, encrypt bool, encryptionKeyID string) (string, bool, error) { + if es := crypto.ParseEncryptedString(f.Secret); es != nil { + bytes, err := es.Decrypt(f.ID.String(), decryptionKeys) + if err != nil { + return "", false, err + } + + return string(bytes), encrypt && es.ShouldReEncrypt(encryptionKeyID), nil + } + + return f.Secret, encrypt, nil +} + func FindFactorByFactorID(conn *storage.Connection, factorID uuid.UUID) (*Factor, error) { var factor Factor err := conn.Find(&factor, factorID) diff --git a/internal/models/factor_test.go b/internal/models/factor_test.go index c6d1f4a70..1ca782ce6 100644 --- a/internal/models/factor_test.go +++ b/internal/models/factor_test.go @@ -37,7 +37,8 @@ func (ts *FactorTestSuite) SetupTest() { require.NoError(ts.T(), err) require.NoError(ts.T(), ts.db.Create(user)) - factor := NewFactor(user, "asimplename", TOTP, FactorStateUnverified, "topsecret") + factor := NewFactor(user, "asimplename", TOTP, FactorStateUnverified) + require.NoError(ts.T(), factor.SetSecret("topsecret", false, "", "")) require.NoError(ts.T(), ts.db.Create(factor)) ts.TestFactor = factor } diff --git a/internal/models/user.go b/internal/models/user.go index 270484e08..721819eab 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -283,7 +283,7 @@ func (u *User) SetPhone(tx *storage.Connection, phone string) error { return tx.UpdateOnly(u, "phone") } -func (u *User) SetPassword(ctx context.Context, password string) error { +func (u *User) SetPassword(ctx context.Context, password string, encrypt bool, encryptionKeyID, encryptionKey string) error { if password == "" { u.EncryptedPassword = "" return nil @@ -295,6 +295,14 @@ func (u *User) SetPassword(ctx context.Context, password string) error { } u.EncryptedPassword = pw + if encrypt { + es, err := crypto.NewEncryptedString(u.ID.String(), []byte(pw), encryptionKeyID, encryptionKey) + if err != nil { + return err + } + + u.EncryptedPassword = es.String() + } return nil } @@ -332,9 +340,22 @@ func (u *User) UpdatePassword(tx *storage.Connection, sessionID *uuid.UUID) erro } // Authenticate a user from a password -func (u *User) Authenticate(ctx context.Context, password string) bool { - err := crypto.CompareHashAndPassword(ctx, u.EncryptedPassword, password) - return err == nil +func (u *User) Authenticate(ctx context.Context, password string, decryptionKeys map[string]string, encrypt bool, encryptionKeyID string) (bool, bool, error) { + hash := u.EncryptedPassword + + es := crypto.ParseEncryptedString(u.EncryptedPassword) + if es != nil { + h, err := es.Decrypt(u.ID.String(), decryptionKeys) + if err != nil { + return false, false, err + } + + hash = string(h) + } + + compareErr := crypto.CompareHashAndPassword(ctx, hash, password) + + return compareErr == nil, encrypt && (es == nil || es.ShouldReEncrypt(encryptionKeyID)), nil } // ConfirmReauthentication resets the reauthentication token diff --git a/internal/models/user_test.go b/internal/models/user_test.go index 6c915f6af..011cf28f0 100644 --- a/internal/models/user_test.go +++ b/internal/models/user_test.go @@ -372,9 +372,9 @@ func (ts *UserTestSuite) TestSetPasswordTooLong() { require.NoError(ts.T(), err) require.NoError(ts.T(), ts.db.Create(user)) - err = user.SetPassword(ts.db.Context(), strings.Repeat("a", crypto.MaxPasswordLength+1)) + err = user.SetPassword(ts.db.Context(), strings.Repeat("a", crypto.MaxPasswordLength+1), false, "", "") require.Error(ts.T(), err) - err = user.SetPassword(ts.db.Context(), strings.Repeat("a", crypto.MaxPasswordLength)) + err = user.SetPassword(ts.db.Context(), strings.Repeat("a", crypto.MaxPasswordLength), false, "", "") require.NoError(ts.T(), err) } From cd7b1919fca99585be0ce842b51ca2a10b926714 Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Wed, 12 Jun 2024 15:54:26 +0200 Subject: [PATCH 07/42] ci: fix doubling of rc version identifier (#1618) ## What kind of change does this PR introduce? See: https://github.com/supabase/ssr/pull/13/files as per title --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8540046a7..2ad87dee4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: # Use git describe tags to identify the number of commits the branch # is ahead of the most recent non-release-candidate tag, which is # part of the rc. value. - RELEASE_VERSION=$MAIN_RELEASE_VERSION-rc.$(node -e "console.log('$(git describe --tags --exclude *rc*)'.split('-')[1])") + RELEASE_VERSION=$MAIN_RELEASE_VERSION-rc.$(node -e "console.log('$(git describe --tags --exclude rc*)'.split('-')[1])") # release-please only ignores releases that have a form like [A-Z0-9], so prefixing with rc RELEASE_NAME="rc$RELEASE_VERSION" From bb992519cdf7578dc02cd7de55e2e6aa09b4c0f3 Mon Sep 17 00:00:00 2001 From: Zach Hawtof Date: Wed, 12 Jun 2024 14:09:26 -0400 Subject: [PATCH 08/42] feat: add support for Slack OAuth V2 (#1591) ## What kind of change does this PR introduce? - Updates the Slack OAuth provider with the new Sign In With Slack V2. - Creates a test for Slack, improving test coverage - Moves the old Slack provider to slack_legacy. Some users might still rely on this provider after the creation of legacy apps is disallowed on June 4th. ## What is the current behavior? Fixes #1294 Current behavior uses the original Slack OAuth V1 which is sunsetting June 4th according to [the changelog](https://api.slack.com/changelog/2024-04-discontinuing-new-creation-of-classic-slack-apps-and-custom-bots) ## What is the new behavior? New behavior now leverages the new [Sign In With Slack](https://api.slack.com/authentication/sign-in-with-slack) (SIWS) on OAuth V2 for Slack authentication. ## Additional context A ticket should be created for ending support on slack_legacy. --------- Co-authored-by: Kang Ming --- CONTRIBUTING.md | 1 + hack/test.env | 8 ++ internal/api/external.go | 2 + internal/api/external_slack_oidc_test.go | 33 ++++++++ internal/api/provider/slack.go | 4 +- internal/api/provider/slack_oidc.go | 99 ++++++++++++++++++++++++ internal/api/settings.go | 2 + internal/api/settings_test.go | 2 + internal/conf/configuration.go | 1 + 9 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 internal/api/external_slack_oidc_test.go create mode 100644 internal/api/provider/slack_oidc.go diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96091c145..f65eca299 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -253,6 +253,7 @@ To see the current settings, make a request to `http://localhost:9999/settings` "facebook": false, "spotify": false, "slack": false, + "slack_oidc": false, "twitch": true, "twitter": false, "email": true, diff --git a/hack/test.env b/hack/test.env index 409940314..35e4b61c8 100644 --- a/hack/test.env +++ b/hack/test.env @@ -60,6 +60,10 @@ GOTRUE_EXTERNAL_LINKEDIN_ENABLED=true GOTRUE_EXTERNAL_LINKEDIN_CLIENT_ID=testclientid GOTRUE_EXTERNAL_LINKEDIN_SECRET=testsecret GOTRUE_EXTERNAL_LINKEDIN_REDIRECT_URI=https://identity.services.netlify.com/callback +GOTRUE_EXTERNAL_LINKEDIN_OIDC_ENABLED=true +GOTRUE_EXTERNAL_LINKEDIN_OIDC_CLIENT_ID=testclientid +GOTRUE_EXTERNAL_LINKEDIN_OIDC_SECRET=testsecret +GOTRUE_EXTERNAL_LINKEDIN_OIDC_REDIRECT_URI=https://identity.services.netlify.com/callback GOTRUE_EXTERNAL_GITLAB_ENABLED=true GOTRUE_EXTERNAL_GITLAB_CLIENT_ID=testclientid GOTRUE_EXTERNAL_GITLAB_SECRET=testsecret @@ -80,6 +84,10 @@ GOTRUE_EXTERNAL_SLACK_ENABLED=true GOTRUE_EXTERNAL_SLACK_CLIENT_ID=testclientid GOTRUE_EXTERNAL_SLACK_SECRET=testsecret GOTRUE_EXTERNAL_SLACK_REDIRECT_URI=https://identity.services.netlify.com/callback +GOTRUE_EXTERNAL_SLACK_OIDC_ENABLED=true +GOTRUE_EXTERNAL_SLACK_OIDC_CLIENT_ID=testclientid +GOTRUE_EXTERNAL_SLACK_OIDC_SECRET=testsecret +GOTRUE_EXTERNAL_SLACK_OIDC_REDIRECT_URI=https://identity.services.netlify.com/callback GOTRUE_EXTERNAL_WORKOS_ENABLED=true GOTRUE_EXTERNAL_WORKOS_CLIENT_ID=testclientid GOTRUE_EXTERNAL_WORKOS_SECRET=testsecret diff --git a/internal/api/external.go b/internal/api/external.go index cf1736f03..a8048fb26 100644 --- a/internal/api/external.go +++ b/internal/api/external.go @@ -558,6 +558,8 @@ func (a *API) Provider(ctx context.Context, name string, scopes string) (provide return provider.NewSpotifyProvider(config.External.Spotify, scopes) case "slack": return provider.NewSlackProvider(config.External.Slack, scopes) + case "slack_oidc": + return provider.NewSlackOIDCProvider(config.External.SlackOIDC, scopes) case "twitch": return provider.NewTwitchProvider(config.External.Twitch, scopes) case "twitter": diff --git a/internal/api/external_slack_oidc_test.go b/internal/api/external_slack_oidc_test.go new file mode 100644 index 000000000..9090581d0 --- /dev/null +++ b/internal/api/external_slack_oidc_test.go @@ -0,0 +1,33 @@ +package api + +import ( + "net/http" + "net/http/httptest" + "net/url" + + jwt "github.com/golang-jwt/jwt" +) + +func (ts *ExternalTestSuite) TestSignupExternalSlackOIDC() { + req := httptest.NewRequest(http.MethodGet, "http://localhost/authorize?provider=slack_oidc", nil) + w := httptest.NewRecorder() + ts.API.handler.ServeHTTP(w, req) + ts.Require().Equal(http.StatusFound, w.Code) + u, err := url.Parse(w.Header().Get("Location")) + ts.Require().NoError(err, "redirect url parse failed") + q := u.Query() + ts.Equal(ts.Config.External.Slack.RedirectURI, q.Get("redirect_uri")) + ts.Equal(ts.Config.External.Slack.ClientID, []string{q.Get("client_id")}) + ts.Equal("code", q.Get("response_type")) + ts.Equal("profile email openid", q.Get("scope")) + + claims := ExternalProviderClaims{} + p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { + return []byte(ts.Config.JWT.Secret), nil + }) + ts.Require().NoError(err) + + ts.Equal("slack_oidc", claims.Provider) + ts.Equal(ts.Config.SiteURL, claims.SiteURL) +} diff --git a/internal/api/provider/slack.go b/internal/api/provider/slack.go index efe318813..40377b0aa 100644 --- a/internal/api/provider/slack.go +++ b/internal/api/provider/slack.go @@ -23,7 +23,7 @@ type slackUser struct { TeamID string `json:"https://slack.com/team_id"` } -// NewSlackProvider creates a Slack account provider. +// NewSlackProvider creates a Slack account provider with Legacy Slack OAuth. func NewSlackProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { if err := ext.ValidateOAuth(); err != nil { return nil, err @@ -71,7 +71,7 @@ func (g slackProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*Use if u.Email != "" { data.Emails = []Email{{ Email: u.Email, - Verified: true, // Slack dosen't provide data on if email is verified. + Verified: true, // Slack doesn't provide data on if email is verified. Primary: true, }} } diff --git a/internal/api/provider/slack_oidc.go b/internal/api/provider/slack_oidc.go new file mode 100644 index 000000000..3c7a5eb62 --- /dev/null +++ b/internal/api/provider/slack_oidc.go @@ -0,0 +1,99 @@ +package provider + +import ( + "context" + "strings" + + "github.com/supabase/auth/internal/conf" + "golang.org/x/oauth2" +) + +const defaultSlackOIDCApiBase = "slack.com" + +type slackOIDCProvider struct { + *oauth2.Config + APIPath string +} + +type slackOIDCUser struct { + ID string `json:"https://slack.com/user_id"` + TeamID string `json:"https://slack.com/team_id"` + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + Name string `json:"name"` + AvatarURL string `json:"picture"` +} + +// NewSlackOIDCProvider creates a Slack account provider with Sign in with Slack. +func NewSlackOIDCProvider(ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) { + if err := ext.ValidateOAuth(); err != nil { + return nil, err + } + + apiPath := chooseHost(ext.URL, defaultSlackOIDCApiBase) + "/api" + authPath := chooseHost(ext.URL, defaultSlackOIDCApiBase) + "/openid" + + // these are required scopes for slack's OIDC flow + // see https://api.slack.com/authentication/sign-in-with-slack#implementation + oauthScopes := []string{ + "profile", + "email", + "openid", + } + + if scopes != "" { + oauthScopes = append(oauthScopes, strings.Split(scopes, ",")...) + } + + return &slackOIDCProvider{ + Config: &oauth2.Config{ + ClientID: ext.ClientID[0], + ClientSecret: ext.Secret, + Endpoint: oauth2.Endpoint{ + AuthURL: authPath + "/connect/authorize", + TokenURL: apiPath + "/openid.connect.token", + }, + Scopes: oauthScopes, + RedirectURL: ext.RedirectURI, + }, + APIPath: apiPath, + }, nil +} + +func (g slackOIDCProvider) GetOAuthToken(code string) (*oauth2.Token, error) { + return g.Exchange(context.Background(), code) +} + +func (g slackOIDCProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) { + var u slackOIDCUser + if err := makeRequest(ctx, tok, g.Config, g.APIPath+"/openid.connect.userInfo", &u); err != nil { + return nil, err + } + + data := &UserProvidedData{} + if u.Email != "" { + data.Emails = []Email{{ + Email: u.Email, + // email_verified is returned as part of the response + // see: https://api.slack.com/authentication/sign-in-with-slack#response + Verified: u.EmailVerified, + Primary: true, + }} + } + + data.Metadata = &Claims{ + Issuer: g.APIPath, + Subject: u.ID, + Name: u.Name, + Picture: u.AvatarURL, + CustomClaims: map[string]interface{}{ + "https://slack.com/team_id": u.TeamID, + }, + + // To be deprecated + AvatarURL: u.AvatarURL, + FullName: u.Name, + ProviderId: u.ID, + } + return data, nil +} diff --git a/internal/api/settings.go b/internal/api/settings.go index 9ea93edb7..16817db10 100644 --- a/internal/api/settings.go +++ b/internal/api/settings.go @@ -21,6 +21,7 @@ type ProviderSettings struct { Notion bool `json:"notion"` Spotify bool `json:"spotify"` Slack bool `json:"slack"` + SlackOIDC bool `json:"slack_oidc"` WorkOS bool `json:"workos"` Twitch bool `json:"twitch"` Twitter bool `json:"twitter"` @@ -62,6 +63,7 @@ func (a *API) Settings(w http.ResponseWriter, r *http.Request) error { Notion: config.External.Notion.Enabled, Spotify: config.External.Spotify.Enabled, Slack: config.External.Slack.Enabled, + SlackOIDC: config.External.SlackOIDC.Enabled, Twitch: config.External.Twitch.Enabled, Twitter: config.External.Twitter.Enabled, WorkOS: config.External.WorkOS.Enabled, diff --git a/internal/api/settings_test.go b/internal/api/settings_test.go index 42a5d9784..767bcf784 100644 --- a/internal/api/settings_test.go +++ b/internal/api/settings_test.go @@ -35,10 +35,12 @@ func TestSettings_DefaultProviders(t *testing.T) { require.True(t, p.Notion) require.True(t, p.Spotify) require.True(t, p.Slack) + require.True(t, p.SlackOIDC) require.True(t, p.Google) require.True(t, p.Kakao) require.True(t, p.Keycloak) require.True(t, p.Linkedin) + require.True(t, p.LinkedinOIDC) require.True(t, p.GitHub) require.True(t, p.GitLab) require.True(t, p.Twitch) diff --git a/internal/conf/configuration.go b/internal/conf/configuration.go index 99f0f1879..d3ba720a0 100644 --- a/internal/conf/configuration.go +++ b/internal/conf/configuration.go @@ -298,6 +298,7 @@ type ProviderConfiguration struct { LinkedinOIDC OAuthProviderConfiguration `json:"linkedin_oidc" envconfig:"LINKEDIN_OIDC"` Spotify OAuthProviderConfiguration `json:"spotify"` Slack OAuthProviderConfiguration `json:"slack"` + SlackOIDC OAuthProviderConfiguration `json:"slack_oidc" envconfig:"SLACK_OIDC"` Twitter OAuthProviderConfiguration `json:"twitter"` Twitch OAuthProviderConfiguration `json:"twitch"` WorkOS OAuthProviderConfiguration `json:"workos"` From 28967aa4b5db2363cc581c9da0d64e974eb7b64c Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 13 Jun 2024 02:14:36 +0800 Subject: [PATCH 09/42] fix: enable rls & update grants for auth tables (#1617) ## What kind of change does this PR introduce? * Previously, users need to grant [these permissions](https://supabase.com/docs/guides/database/database-advisors?lint=0002_auth_users_exposed#security-invoker-view-with-rls-on-authusers) to create views with RLS for tables in the auth schema. * This also unblocks our efforts to revoke `supabase_auth_admin` membership from `postgres` to prevent cases where the `auth.schema_migrations` table is accidentally truncated by the user - causing migrations to be rerun unnecessarily. * Bug fix, feature, docs update, ... ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- ...0612123726_enable_rls_update_grants.up.sql | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 migrations/20240612123726_enable_rls_update_grants.up.sql diff --git a/migrations/20240612123726_enable_rls_update_grants.up.sql b/migrations/20240612123726_enable_rls_update_grants.up.sql new file mode 100644 index 000000000..9201e8496 --- /dev/null +++ b/migrations/20240612123726_enable_rls_update_grants.up.sql @@ -0,0 +1,36 @@ +do $$ begin + -- enable RLS policy on auth tables + alter table {{ index .Options "Namespace" }}.schema_migrations enable row level security; + alter table {{ index .Options "Namespace" }}.instances enable row level security; + alter table {{ index .Options "Namespace" }}.users enable row level security; + alter table {{ index .Options "Namespace" }}.audit_log_entries enable row level security; + alter table {{ index .Options "Namespace" }}.saml_relay_states enable row level security; + alter table {{ index .Options "Namespace" }}.refresh_tokens enable row level security; + alter table {{ index .Options "Namespace" }}.mfa_factors enable row level security; + alter table {{ index .Options "Namespace" }}.sessions enable row level security; + alter table {{ index .Options "Namespace" }}.sso_providers enable row level security; + alter table {{ index .Options "Namespace" }}.sso_domains enable row level security; + alter table {{ index .Options "Namespace" }}.mfa_challenges enable row level security; + alter table {{ index .Options "Namespace" }}.mfa_amr_claims enable row level security; + alter table {{ index .Options "Namespace" }}.saml_providers enable row level security; + alter table {{ index .Options "Namespace" }}.flow_state enable row level security; + alter table {{ index .Options "Namespace" }}.identities enable row level security; + alter table {{ index .Options "Namespace" }}.one_time_tokens enable row level security; + -- allow postgres role to select from auth tables and allow it to grant select to other roles + grant select on {{ index .Options "Namespace" }}.schema_migrations to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.instances to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.users to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.audit_log_entries to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.saml_relay_states to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.refresh_tokens to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.mfa_factors to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.sessions to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.sso_providers to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.sso_domains to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.mfa_challenges to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.mfa_amr_claims to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.saml_providers to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.flow_state to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.identities to postgres with grant option; + grant select on {{ index .Options "Namespace" }}.one_time_tokens to postgres with grant option; +end $$; From 3cd00ee288b6c88577f07b4e2cf1e959ae172404 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:41:07 +0200 Subject: [PATCH 10/42] chore(master): release 2.154.0 (#1610) :robot: I have created a release *beep* *boop* --- ## [2.154.0](https://github.com/supabase/auth/compare/v2.153.0...v2.154.0) (2024-06-12) ### Features * add max length check for email ([#1508](https://github.com/supabase/auth/issues/1508)) ([f9c13c0](https://github.com/supabase/auth/commit/f9c13c0ad5c556bede49d3e0f6e5f58ca26161c3)) * add support for Slack OAuth V2 ([#1591](https://github.com/supabase/auth/issues/1591)) ([bb99251](https://github.com/supabase/auth/commit/bb992519cdf7578dc02cd7de55e2e6aa09b4c0f3)) * encrypt sensitive columns ([#1593](https://github.com/supabase/auth/issues/1593)) ([e4a4758](https://github.com/supabase/auth/commit/e4a475820b2dc1f985bd37df15a8ab9e781626f5)) * upgrade otel to v1.26 ([#1585](https://github.com/supabase/auth/issues/1585)) ([cdd13ad](https://github.com/supabase/auth/commit/cdd13adec02eb0c9401bc55a2915c1005d50dea1)) * use largest avatar from spotify instead ([#1210](https://github.com/supabase/auth/issues/1210)) ([4f9994b](https://github.com/supabase/auth/commit/4f9994bf792c3887f2f45910b11a9c19ee3a896b)), closes [#1209](https://github.com/supabase/auth/issues/1209) ### Bug Fixes * define search path in auth functions ([#1616](https://github.com/supabase/auth/issues/1616)) ([357bda2](https://github.com/supabase/auth/commit/357bda23cb2abd12748df80a9d27288aa548534d)) * enable rls & update grants for auth tables ([#1617](https://github.com/supabase/auth/issues/1617)) ([28967aa](https://github.com/supabase/auth/commit/28967aa4b5db2363cc581c9da0d64e974eb7b64c)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4315f21cb..f64d3be31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [2.154.0](https://github.com/supabase/auth/compare/v2.153.0...v2.154.0) (2024-06-12) + + +### Features + +* add max length check for email ([#1508](https://github.com/supabase/auth/issues/1508)) ([f9c13c0](https://github.com/supabase/auth/commit/f9c13c0ad5c556bede49d3e0f6e5f58ca26161c3)) +* add support for Slack OAuth V2 ([#1591](https://github.com/supabase/auth/issues/1591)) ([bb99251](https://github.com/supabase/auth/commit/bb992519cdf7578dc02cd7de55e2e6aa09b4c0f3)) +* encrypt sensitive columns ([#1593](https://github.com/supabase/auth/issues/1593)) ([e4a4758](https://github.com/supabase/auth/commit/e4a475820b2dc1f985bd37df15a8ab9e781626f5)) +* upgrade otel to v1.26 ([#1585](https://github.com/supabase/auth/issues/1585)) ([cdd13ad](https://github.com/supabase/auth/commit/cdd13adec02eb0c9401bc55a2915c1005d50dea1)) +* use largest avatar from spotify instead ([#1210](https://github.com/supabase/auth/issues/1210)) ([4f9994b](https://github.com/supabase/auth/commit/4f9994bf792c3887f2f45910b11a9c19ee3a896b)), closes [#1209](https://github.com/supabase/auth/issues/1209) + + +### Bug Fixes + +* define search path in auth functions ([#1616](https://github.com/supabase/auth/issues/1616)) ([357bda2](https://github.com/supabase/auth/commit/357bda23cb2abd12748df80a9d27288aa548534d)) +* enable rls & update grants for auth tables ([#1617](https://github.com/supabase/auth/issues/1617)) ([28967aa](https://github.com/supabase/auth/commit/28967aa4b5db2363cc581c9da0d64e974eb7b64c)) + ## [2.153.0](https://github.com/supabase/auth/compare/v2.152.0...v2.153.0) (2024-06-04) From f5c6fcd9c3fee0f793f96880a8caebc5b5cb0916 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Mon, 17 Jun 2024 17:01:15 +0800 Subject: [PATCH 11/42] fix: admin user update should update is_anonymous field (#1623) ## What kind of change does this PR introduce? * Fixes #1578 --- internal/api/admin.go | 29 +++++++++--- internal/api/anonymous_test.go | 84 ++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 6 deletions(-) diff --git a/internal/api/admin.go b/internal/api/admin.go index 053a75d35..1cda8e264 100644 --- a/internal/api/admin.go +++ b/internal/api/admin.go @@ -214,8 +214,9 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { // if the user doesn't have an existing email // then updating the user's email should create a new email identity i, terr := a.createNewIdentity(tx, user, "email", structs.Map(provider.Claims{ - Subject: user.ID.String(), - Email: params.Email, + Subject: user.ID.String(), + Email: params.Email, + EmailVerified: params.EmailConfirm, })) if terr != nil { return terr @@ -224,11 +225,19 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { } else { // update the existing email identity if terr := identity.UpdateIdentityData(tx, map[string]interface{}{ - "email": params.Email, + "email": params.Email, + "email_verified": params.EmailConfirm, }); terr != nil { return terr } } + if user.IsAnonymous && params.EmailConfirm { + user.IsAnonymous = false + if terr := tx.UpdateOnly(user, "is_anonymous"); terr != nil { + return terr + } + } + if terr := user.SetEmail(tx, params.Email); terr != nil { return terr } @@ -241,8 +250,9 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { // if the user doesn't have an existing phone // then updating the user's phone should create a new phone identity identity, terr := a.createNewIdentity(tx, user, "phone", structs.Map(provider.Claims{ - Subject: user.ID.String(), - Phone: params.Phone, + Subject: user.ID.String(), + Phone: params.Phone, + PhoneVerified: params.PhoneConfirm, })) if terr != nil { return terr @@ -251,11 +261,18 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { } else { // update the existing phone identity if terr := identity.UpdateIdentityData(tx, map[string]interface{}{ - "phone": params.Phone, + "phone": params.Phone, + "phone_verified": params.PhoneConfirm, }); terr != nil { return terr } } + if user.IsAnonymous && params.PhoneConfirm { + user.IsAnonymous = false + if terr := tx.UpdateOnly(user, "is_anonymous"); terr != nil { + return terr + } + } if terr := user.SetPhone(tx, params.Phone); terr != nil { return terr } diff --git a/internal/api/anonymous_test.go b/internal/api/anonymous_test.go index fdee4cc07..92877cf19 100644 --- a/internal/api/anonymous_test.go +++ b/internal/api/anonymous_test.go @@ -8,6 +8,8 @@ import ( "net/http/httptest" "testing" + "github.com/gofrs/uuid" + jwt "github.com/golang-jwt/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -222,3 +224,85 @@ func (ts *AnonymousTestSuite) TestRateLimitAnonymousSignups() { ts.API.handler.ServeHTTP(w, req) assert.Equal(ts.T(), http.StatusBadRequest, w.Code) } + +func (ts *AnonymousTestSuite) TestAdminUpdateAnonymousUser() { + claims := &AccessTokenClaims{ + Role: "supabase_admin", + } + adminJwt, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(ts.Config.JWT.Secret)) + require.NoError(ts.T(), err) + + u1, err := models.NewUser("", "", "", ts.Config.JWT.Aud, nil) + require.NoError(ts.T(), err) + u1.IsAnonymous = true + require.NoError(ts.T(), ts.API.db.Create(u1)) + + u2, err := models.NewUser("", "", "", ts.Config.JWT.Aud, nil) + require.NoError(ts.T(), err) + u2.IsAnonymous = true + require.NoError(ts.T(), ts.API.db.Create(u2)) + + cases := []struct { + desc string + userId uuid.UUID + body map[string]interface{} + expected map[string]interface{} + expectedIdentities int + }{ + { + desc: "update anonymous user with email and email confirm true", + userId: u1.ID, + body: map[string]interface{}{ + "email": "foo@example.com", + "email_confirm": true, + }, + expected: map[string]interface{}{ + "email": "foo@example.com", + "is_anonymous": false, + }, + expectedIdentities: 1, + }, + { + desc: "update anonymous user with email and email confirm false", + userId: u2.ID, + body: map[string]interface{}{ + "email": "bar@example.com", + "email_confirm": false, + }, + expected: map[string]interface{}{ + "email": "bar@example.com", + "is_anonymous": true, + }, + expectedIdentities: 1, + }, + } + + for _, c := range cases { + ts.Run(c.desc, func() { + // Request body + var buffer bytes.Buffer + require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(c.body)) + + req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/admin/users/%s", c.userId), &buffer) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", adminJwt)) + + w := httptest.NewRecorder() + ts.API.handler.ServeHTTP(w, req) + require.Equal(ts.T(), http.StatusOK, w.Code) + + var data models.User + require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data)) + + require.NotNil(ts.T(), data) + require.Len(ts.T(), data.Identities, c.expectedIdentities) + + actual := map[string]interface{}{ + "email": data.GetEmail(), + "is_anonymous": data.IsAnonymous, + } + + require.Equal(ts.T(), c.expected, actual) + }) + } +} From 06464c013571253d1f18f7ae5e840826c4bd84a7 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Mon, 17 Jun 2024 17:02:13 +0800 Subject: [PATCH 12/42] fix: add ip based limiter (#1622) ## What kind of change does this PR introduce? * Adds ip-based rate limiting on all endpoints that send OTPs either through email or phone with the config `GOTRUE_RATE_LIMIT_OTP` * IP-based rate limiting should always come before the shared limiter, so as to prevent the quota of the shared limiter from being consumed too quickly by the same ip-address --- internal/api/api.go | 55 +++++++++++-- internal/api/middleware_test.go | 134 ++++++++++++++++++++++++++++++++ internal/conf/configuration.go | 1 + 3 files changed, 183 insertions(+), 7 deletions(-) diff --git a/internal/api/api.go b/internal/api/api.go index 8613a05dc..6e49e8a19 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -136,9 +136,14 @@ func NewAPIWithVersion(globalConfig *conf.GlobalConfiguration, db *storage.Conne r.With(sharedLimiter).With(api.requireAdminCredentials).Post("/invite", api.Invite) r.With(sharedLimiter).With(api.verifyCaptcha).Route("/signup", func(r *router) { // rate limit per hour - limiter := tollbooth.NewLimiter(api.config.RateLimitAnonymousUsers/(60*60), &limiter.ExpirableOptions{ + limitAnonymousSignIns := tollbooth.NewLimiter(api.config.RateLimitAnonymousUsers/(60*60), &limiter.ExpirableOptions{ DefaultExpirationTTL: time.Hour, }).SetBurst(int(api.config.RateLimitAnonymousUsers)).SetMethods([]string{"POST"}) + + limitSignups := tollbooth.NewLimiter(api.config.RateLimitOtp/(60*5), &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }).SetBurst(30) + r.Post("/", func(w http.ResponseWriter, r *http.Request) error { params := &SignupParams{} if err := retrieveRequestParams(r, params); err != nil { @@ -148,19 +153,50 @@ func NewAPIWithVersion(globalConfig *conf.GlobalConfiguration, db *storage.Conne if !api.config.External.AnonymousUsers.Enabled { return unprocessableEntityError(ErrorCodeAnonymousProviderDisabled, "Anonymous sign-ins are disabled") } - if _, err := api.limitHandler(limiter)(w, r); err != nil { + if _, err := api.limitHandler(limitAnonymousSignIns)(w, r); err != nil { return err } return api.SignupAnonymously(w, r) } + + // apply ip-based rate limiting on otps + if _, err := api.limitHandler(limitSignups)(w, r); err != nil { + return err + } + // apply shared rate limiting on email / phone + if _, err := sharedLimiter(w, r); err != nil { + return err + } return api.Signup(w, r) }) }) - r.With(sharedLimiter).With(api.verifyCaptcha).With(api.requireEmailProvider).Post("/recover", api.Recover) - r.With(sharedLimiter).With(api.verifyCaptcha).Post("/resend", api.Resend) - r.With(sharedLimiter).With(api.verifyCaptcha).Post("/magiclink", api.MagicLink) + r.With(api.limitHandler( + // Allow requests at the specified rate per 5 minutes + tollbooth.NewLimiter(api.config.RateLimitOtp/(60*5), &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }).SetBurst(30), + )).With(sharedLimiter).With(api.verifyCaptcha).With(api.requireEmailProvider).Post("/recover", api.Recover) - r.With(sharedLimiter).With(api.verifyCaptcha).Post("/otp", api.Otp) + r.With(api.limitHandler( + // Allow requests at the specified rate per 5 minutes + tollbooth.NewLimiter(api.config.RateLimitOtp/(60*5), &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }).SetBurst(30), + )).With(sharedLimiter).With(api.verifyCaptcha).Post("/resend", api.Resend) + + r.With(api.limitHandler( + // Allow requests at the specified rate per 5 minutes + tollbooth.NewLimiter(api.config.RateLimitOtp/(60*5), &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }).SetBurst(30), + )).With(sharedLimiter).With(api.verifyCaptcha).Post("/magiclink", api.MagicLink) + + r.With(api.limitHandler( + // Allow requests at the specified rate per 5 minutes + tollbooth.NewLimiter(api.config.RateLimitOtp/(60*5), &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }).SetBurst(30), + )).With(sharedLimiter).With(api.verifyCaptcha).Post("/otp", api.Otp) r.With(api.limitHandler( // Allow requests at the specified rate per 5 minutes. @@ -187,7 +223,12 @@ func NewAPIWithVersion(globalConfig *conf.GlobalConfiguration, db *storage.Conne r.With(api.requireAuthentication).Route("/user", func(r *router) { r.Get("/", api.UserGet) - r.With(sharedLimiter).Put("/", api.UserUpdate) + r.With(api.limitHandler( + // Allow requests at the specified rate per 5 minutes + tollbooth.NewLimiter(api.config.RateLimitOtp/(60*5), &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }).SetBurst(30), + )).With(sharedLimiter).Put("/", api.UserUpdate) r.Route("/identities", func(r *router) { r.Use(api.requireManualLinkingEnabled) diff --git a/internal/api/middleware_test.go b/internal/api/middleware_test.go index a9d908c32..4d0e327f3 100644 --- a/internal/api/middleware_test.go +++ b/internal/api/middleware_test.go @@ -11,6 +11,8 @@ import ( "testing" "time" + "github.com/didip/tollbooth/v5" + "github.com/didip/tollbooth/v5/limiter" jwt "github.com/golang-jwt/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -356,3 +358,135 @@ func TestTimeoutResponseWriter(t *testing.T) { require.Equal(t, w1.Result(), w2.Result()) } + +func (ts *MiddlewareTestSuite) TestLimitHandler() { + ts.Config.RateLimitHeader = "X-Rate-Limit" + lmt := tollbooth.NewLimiter(5, &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }) + + okHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + b, _ := json.Marshal(map[string]interface{}{"message": "ok"}) + w.Write([]byte(b)) + }) + + for i := 0; i < 5; i++ { + req := httptest.NewRequest(http.MethodGet, "http://localhost", nil) + req.Header.Add(ts.Config.RateLimitHeader, "0.0.0.0") + w := httptest.NewRecorder() + ts.API.limitHandler(lmt).handler(okHandler).ServeHTTP(w, req) + require.Equal(ts.T(), http.StatusOK, w.Code) + + var data map[string]interface{} + require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data)) + require.Equal(ts.T(), "ok", data["message"]) + } + + // 6th request should fail and return a rate limit exceeded error + req := httptest.NewRequest(http.MethodGet, "http://localhost", nil) + req.Header.Add(ts.Config.RateLimitHeader, "0.0.0.0") + w := httptest.NewRecorder() + ts.API.limitHandler(lmt).handler(okHandler).ServeHTTP(w, req) + require.Equal(ts.T(), http.StatusTooManyRequests, w.Code) +} + +func (ts *MiddlewareTestSuite) TestLimitHandlerWithSharedLimiter() { + // setup config for shared limiter and ip-based limiter to work + ts.Config.RateLimitHeader = "X-Rate-Limit" + ts.Config.External.Email.Enabled = true + ts.Config.External.Phone.Enabled = true + ts.Config.Mailer.Autoconfirm = false + ts.Config.Sms.Autoconfirm = false + + ipBasedLimiter := func(max float64) *limiter.Limiter { + return tollbooth.NewLimiter(max, &limiter.ExpirableOptions{ + DefaultExpirationTTL: time.Hour, + }) + } + + okHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + cases := []struct { + desc string + sharedLimiterConfig *conf.GlobalConfiguration + ipBasedLimiterConfig float64 + body map[string]interface{} + expectedErrorCode string + }{ + { + desc: "Exceed ip-based rate limit before shared limiter", + sharedLimiterConfig: &conf.GlobalConfiguration{ + RateLimitEmailSent: 10, + RateLimitSmsSent: 10, + }, + ipBasedLimiterConfig: 1, + body: map[string]interface{}{ + "email": "foo@example.com", + }, + expectedErrorCode: ErrorCodeOverRequestRateLimit, + }, + { + desc: "Exceed email shared limiter", + sharedLimiterConfig: &conf.GlobalConfiguration{ + RateLimitEmailSent: 1, + RateLimitSmsSent: 1, + }, + ipBasedLimiterConfig: 10, + body: map[string]interface{}{ + "email": "foo@example.com", + }, + expectedErrorCode: ErrorCodeOverEmailSendRateLimit, + }, + { + desc: "Exceed sms shared limiter", + sharedLimiterConfig: &conf.GlobalConfiguration{ + RateLimitEmailSent: 1, + RateLimitSmsSent: 1, + }, + ipBasedLimiterConfig: 10, + body: map[string]interface{}{ + "phone": "123456789", + }, + expectedErrorCode: ErrorCodeOverSMSSendRateLimit, + }, + } + + for _, c := range cases { + ts.Run(c.desc, func() { + ts.Config.RateLimitEmailSent = c.sharedLimiterConfig.RateLimitEmailSent + ts.Config.RateLimitSmsSent = c.sharedLimiterConfig.RateLimitSmsSent + lmt := ts.API.limitHandler(ipBasedLimiter(c.ipBasedLimiterConfig)) + sharedLimiter := ts.API.limitEmailOrPhoneSentHandler() + + // get the minimum amount to reach the threshold just before the rate limit is exceeded + threshold := min(c.sharedLimiterConfig.RateLimitEmailSent, c.sharedLimiterConfig.RateLimitSmsSent, c.ipBasedLimiterConfig) + for i := 0; i < int(threshold); i++ { + var buffer bytes.Buffer + require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(c.body)) + req := httptest.NewRequest(http.MethodPost, "http://localhost", &buffer) + req.Header.Add(ts.Config.RateLimitHeader, "0.0.0.0") + + w := httptest.NewRecorder() + lmt.handler(sharedLimiter.handler(okHandler)).ServeHTTP(w, req) + require.Equal(ts.T(), http.StatusOK, w.Code) + } + + var buffer bytes.Buffer + require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(c.body)) + req := httptest.NewRequest(http.MethodPost, "http://localhost", &buffer) + req.Header.Add(ts.Config.RateLimitHeader, "0.0.0.0") + + // check if the rate limit is exceeded with the expected error code + w := httptest.NewRecorder() + lmt.handler(sharedLimiter.handler(okHandler)).ServeHTTP(w, req) + require.Equal(ts.T(), http.StatusTooManyRequests, w.Code) + + var data map[string]interface{} + require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data)) + require.Equal(ts.T(), c.expectedErrorCode, data["error_code"]) + }) + } +} diff --git a/internal/conf/configuration.go b/internal/conf/configuration.go index d3ba720a0..35024ea52 100644 --- a/internal/conf/configuration.go +++ b/internal/conf/configuration.go @@ -223,6 +223,7 @@ type GlobalConfiguration struct { RateLimitTokenRefresh float64 `split_words:"true" default:"150"` RateLimitSso float64 `split_words:"true" default:"30"` RateLimitAnonymousUsers float64 `split_words:"true" default:"30"` + RateLimitOtp float64 `split_words:"true" default:"30"` SiteURL string `json:"site_url" split_words:"true" required:"true"` URIAllowList []string `json:"uri_allow_list" split_words:"true"` From bca0ea72831c890df5a5bf7207a80f5f8e594179 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 02:44:20 +0200 Subject: [PATCH 13/42] chore(master): release 2.154.1 (#1624) :robot: I have created a release *beep* *boop* --- ## [2.154.1](https://github.com/supabase/auth/compare/v2.154.0...v2.154.1) (2024-06-17) ### Bug Fixes * add ip based limiter ([#1622](https://github.com/supabase/auth/issues/1622)) ([06464c0](https://github.com/supabase/auth/commit/06464c013571253d1f18f7ae5e840826c4bd84a7)) * admin user update should update is_anonymous field ([#1623](https://github.com/supabase/auth/issues/1623)) ([f5c6fcd](https://github.com/supabase/auth/commit/f5c6fcd9c3fee0f793f96880a8caebc5b5cb0916)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f64d3be31..1f3599fe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [2.154.1](https://github.com/supabase/auth/compare/v2.154.0...v2.154.1) (2024-06-17) + + +### Bug Fixes + +* add ip based limiter ([#1622](https://github.com/supabase/auth/issues/1622)) ([06464c0](https://github.com/supabase/auth/commit/06464c013571253d1f18f7ae5e840826c4bd84a7)) +* admin user update should update is_anonymous field ([#1623](https://github.com/supabase/auth/issues/1623)) ([f5c6fcd](https://github.com/supabase/auth/commit/f5c6fcd9c3fee0f793f96880a8caebc5b5cb0916)) + ## [2.154.0](https://github.com/supabase/auth/compare/v2.153.0...v2.154.0) (2024-06-12) From 930aa3edb633823d4510c2aff675672df06f1211 Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Thu, 20 Jun 2024 10:12:38 +0200 Subject: [PATCH 14/42] fix: publish to ghcr.io/supabase/auth (#1626) ## What kind of change does this PR introduce? Fix #1625 --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c92959778..133163f1c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -25,6 +25,7 @@ jobs: supabase/gotrue public.ecr.aws/supabase/gotrue ghcr.io/supabase/gotrue + ghcr.io/supabase/auth 436098097459.dkr.ecr.us-east-1.amazonaws.com/gotrue 646182064048.dkr.ecr.us-east-1.amazonaws.com/gotrue supabase/auth From e81c25d19551fdebfc5197d96bc220ddb0f8227b Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Thu, 20 Jun 2024 13:54:37 +0200 Subject: [PATCH 15/42] fix: update MaxFrequency error message to reflect number of seconds (#1540) ## What kind of change does this PR introduce? Currently we use a constant value on number of seconds left before a developer can send a follow up email confirmation. This can prove confusing if developer has a custom setting for `MaxFrequency` on `Sms` or `SMTP` We change some core email related routes to show the exact number of seconds. This includes `signup`, `magic_link` and `email_change` The rest will follow should this change roll out smoothly. Tested the three flows locally by triggering the max frequency limit and checking that the number of seconds show up as expected. --- internal/api/errors.go | 7 +++++++ internal/api/magic_link.go | 2 +- internal/api/signup.go | 5 +---- internal/api/user.go | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/internal/api/errors.go b/internal/api/errors.go index 2d40a53f4..e821409a7 100644 --- a/internal/api/errors.go +++ b/internal/api/errors.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "runtime/debug" + "time" "github.com/pkg/errors" "github.com/supabase/auth/internal/observability" @@ -312,3 +313,9 @@ func HandleResponseError(err error, w http.ResponseWriter, r *http.Request) { } } } + +func generateFrequencyLimitErrorMessage(timeStamp *time.Time, maxFrequency time.Duration) string { + now := time.Now() + left := timeStamp.Add(maxFrequency).Sub(now) / time.Second + return fmt.Sprintf("For security purposes, you can only request this after %d seconds.", left) +} diff --git a/internal/api/magic_link.go b/internal/api/magic_link.go index e197d72f6..eeabafd39 100644 --- a/internal/api/magic_link.go +++ b/internal/api/magic_link.go @@ -142,7 +142,7 @@ func (a *API) MagicLink(w http.ResponseWriter, r *http.Request) error { }) if err != nil { if errors.Is(err, MaxFrequencyLimitError) { - return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, "For security purposes, you can only request this once every 60 seconds") + return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, generateFrequencyLimitErrorMessage(user.RecoverySentAt, config.SMTP.MaxFrequency)) } return internalServerError("Error sending magic link").WithInternalError(err) } diff --git a/internal/api/signup.go b/internal/api/signup.go index 75c287cff..b396178db 100644 --- a/internal/api/signup.go +++ b/internal/api/signup.go @@ -2,7 +2,6 @@ package api import ( "context" - "fmt" "net/http" "time" @@ -246,9 +245,7 @@ func (a *API) Signup(w http.ResponseWriter, r *http.Request) error { } if terr = a.sendConfirmation(r, tx, user, flowType); terr != nil { if errors.Is(terr, MaxFrequencyLimitError) { - now := time.Now() - left := user.ConfirmationSentAt.Add(config.SMTP.MaxFrequency).Sub(now) / time.Second - return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, fmt.Sprintf("For security purposes, you can only request this after %d seconds.", left)) + return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, generateFrequencyLimitErrorMessage(user.ConfirmationSentAt, config.SMTP.MaxFrequency)) } return internalServerError("Error sending confirmation mail").WithInternalError(terr) } diff --git a/internal/api/user.go b/internal/api/user.go index 33c35aa57..e76b77453 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -214,7 +214,7 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { } if terr = a.sendEmailChange(r, tx, user, params.Email, flowType); terr != nil { if errors.Is(terr, MaxFrequencyLimitError) { - return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, "For security purposes, you can only request this once every 60 seconds") + return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, generateFrequencyLimitErrorMessage(user.EmailChangeSentAt, config.SMTP.MaxFrequency)) } return internalServerError("Error sending change email").WithInternalError(terr) } From 155e87ef8129366d665968f64d1fc66676d07e16 Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Mon, 24 Jun 2024 17:41:07 +0200 Subject: [PATCH 16/42] fix: revert define search path in auth functions (#1634) Reverts supabase/auth#1616 Follow up to #1633 - more context there and in this discussion: https://supabase.slack.com/archives/C07A55TKL3S/p1719237535404369 --- .../20240612114525_set_search_path.up.sql | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 migrations/20240612114525_set_search_path.up.sql diff --git a/migrations/20240612114525_set_search_path.up.sql b/migrations/20240612114525_set_search_path.up.sql deleted file mode 100644 index 5d6ff2081..000000000 --- a/migrations/20240612114525_set_search_path.up.sql +++ /dev/null @@ -1,43 +0,0 @@ --- set the search_path to an empty string to force fully qualified names in the function -do $$ -begin - -- auth.uid() function - create or replace function auth.uid() - returns uuid - set search_path to '' - as $func$ - select nullif(current_setting('request.jwt.claim.sub', true), '')::uuid; - $func$ language sql stable; - - -- auth.role() function - create or replace function {{ index .Options "Namespace" }}.role() - returns text - set search_path to '' - as $func$ - select nullif(current_setting('request.jwt.claim.role', true), '')::text; - $func$ language sql stable; - - -- auth.email() function - create or replace function {{ index .Options "Namespace" }}.email() - returns text - set search_path to '' - as $func$ - select - coalesce( - current_setting('request.jwt.claim.email', true), - (current_setting('request.jwt.claims', true)::jsonb ->> 'email') - )::text - $func$ language sql stable; - - -- auth.jwt() function - create or replace function {{ index .Options "Namespace" }}.jwt() - returns jsonb - set search_path to '' - as $func$ - select - coalesce( - nullif(current_setting('request.jwt.claim', true), ''), - nullif(current_setting('request.jwt.claims', true), '') - )::jsonb; - $func$ language sql stable; -end $$; From 32afa7e5ade6609055f44d8f09aaef69eeaed051 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 17:44:17 +0200 Subject: [PATCH 17/42] chore(master): release 2.154.2 (#1628) :robot: I have created a release *beep* *boop* --- ## [2.154.2](https://github.com/supabase/auth/compare/v2.154.1...v2.154.2) (2024-06-24) ### Bug Fixes * publish to ghcr.io/supabase/auth ([#1626](https://github.com/supabase/auth/issues/1626)) ([930aa3e](https://github.com/supabase/auth/commit/930aa3edb633823d4510c2aff675672df06f1211)), closes [#1625](https://github.com/supabase/auth/issues/1625) * revert define search path in auth functions ([#1634](https://github.com/supabase/auth/issues/1634)) ([155e87e](https://github.com/supabase/auth/commit/155e87ef8129366d665968f64d1fc66676d07e16)) * update MaxFrequency error message to reflect number of seconds ([#1540](https://github.com/supabase/auth/issues/1540)) ([e81c25d](https://github.com/supabase/auth/commit/e81c25d19551fdebfc5197d96bc220ddb0f8227b)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f3599fe1..c708a5779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [2.154.2](https://github.com/supabase/auth/compare/v2.154.1...v2.154.2) (2024-06-24) + + +### Bug Fixes + +* publish to ghcr.io/supabase/auth ([#1626](https://github.com/supabase/auth/issues/1626)) ([930aa3e](https://github.com/supabase/auth/commit/930aa3edb633823d4510c2aff675672df06f1211)), closes [#1625](https://github.com/supabase/auth/issues/1625) +* revert define search path in auth functions ([#1634](https://github.com/supabase/auth/issues/1634)) ([155e87e](https://github.com/supabase/auth/commit/155e87ef8129366d665968f64d1fc66676d07e16)) +* update MaxFrequency error message to reflect number of seconds ([#1540](https://github.com/supabase/auth/issues/1540)) ([e81c25d](https://github.com/supabase/auth/commit/e81c25d19551fdebfc5197d96bc220ddb0f8227b)) + ## [2.154.1](https://github.com/supabase/auth/compare/v2.154.0...v2.154.1) (2024-06-17) From d8b47f9d3f0dc8f97ad1de49e45f452ebc726481 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 26 Jun 2024 18:03:47 +0800 Subject: [PATCH 18/42] fix: improve mfa verify logs (#1635) ## What kind of change does this PR introduce? * Upgrade the totp library to the latest version * Improve logging when mfa verification fails by returning the validation error internally as well as logging the code used --- go.mod | 2 +- go.sum | 2 ++ internal/api/mfa.go | 11 +++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3911c8a69..35ed75024 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/mitchellh/mapstructure v1.1.2 github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 github.com/pkg/errors v0.9.1 - github.com/pquerna/otp v1.3.0 + github.com/pquerna/otp v1.4.0 github.com/rs/cors v1.9.0 github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35 github.com/sethvargo/go-password v0.2.0 diff --git a/go.sum b/go.sum index fdfb47a9b..0c289a276 100644 --- a/go.sum +++ b/go.sum @@ -254,6 +254,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= +github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= diff --git a/internal/api/mfa.go b/internal/api/mfa.go index d2e8295f7..df70c6b51 100644 --- a/internal/api/mfa.go +++ b/internal/api/mfa.go @@ -5,11 +5,13 @@ import ( "fmt" "net/http" "net/url" + "time" "github.com/aaronarduino/goqrsvg" svg "github.com/ajstarks/svgo" "github.com/boombuler/barcode/qr" "github.com/gofrs/uuid" + "github.com/pquerna/otp" "github.com/pquerna/otp/totp" "github.com/supabase/auth/internal/crypto" "github.com/supabase/auth/internal/hooks" @@ -244,7 +246,12 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { return internalServerError("Database error verifying MFA TOTP secret").WithInternalError(err) } - valid := totp.Validate(params.Code, secret) + valid, verr := totp.ValidateCustom(params.Code, secret, time.Now().UTC(), totp.ValidateOpts{ + Period: 30, + Skew: 1, + Digits: otp.DigitsSix, + Algorithm: otp.AlgorithmSHA1, + }) if config.Hook.MFAVerificationAttempt.Enabled { input := hooks.MFAVerificationAttemptInput{ @@ -282,7 +289,7 @@ func (a *API) VerifyFactor(w http.ResponseWriter, r *http.Request) error { return err } } - return unprocessableEntityError(ErrorCodeMFAVerificationFailed, "Invalid TOTP code entered") + return unprocessableEntityError(ErrorCodeMFAVerificationFailed, "Invalid TOTP code entered").WithInternalError(verr) } var token *AccessTokenResponse From bbecbd61a46b0c528b1191f48d51f166c06f4b16 Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Thu, 27 Jun 2024 15:07:11 +0200 Subject: [PATCH 19/42] fix: use pointer for `user.EncryptedPassword` (#1637) Makes sure that `NULL` values in the `auth.users.encrypted_password` column are not met with SQL serialization errors. --- internal/api/invite_test.go | 2 +- internal/api/user.go | 2 +- internal/api/verify.go | 4 ++-- internal/models/user.go | 33 ++++++++++++++++++++++++--------- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/internal/api/invite_test.go b/internal/api/invite_test.go index 1d502adc8..864463d10 100644 --- a/internal/api/invite_test.go +++ b/internal/api/invite_test.go @@ -207,7 +207,7 @@ func (ts *InviteTestSuite) TestVerifyInvite() { now := time.Now() user.InvitedAt = &now user.ConfirmationSentAt = &now - user.EncryptedPassword = "" + user.EncryptedPassword = nil user.ConfirmationToken = crypto.GenerateTokenHash(c.email, c.requestBody["token"].(string)) require.NoError(ts.T(), err) require.NoError(ts.T(), ts.API.db.Create(user)) diff --git a/internal/api/user.go b/internal/api/user.go index e76b77453..6fd8d34e5 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -155,7 +155,7 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { if password != "" { isSamePassword := false - if user.EncryptedPassword != "" { + if user.HasPassword() { auth, _, err := user.Authenticate(ctx, password, config.Security.DBEncryption.DecryptionKeys, false, "") if err != nil { return err diff --git a/internal/api/verify.go b/internal/api/verify.go index 91df8bc21..1040ce7c6 100644 --- a/internal/api/verify.go +++ b/internal/api/verify.go @@ -306,7 +306,7 @@ func (a *API) verifyPost(w http.ResponseWriter, r *http.Request, params *VerifyP func (a *API) signupVerify(r *http.Request, ctx context.Context, conn *storage.Connection, user *models.User) (*models.User, error) { config := a.config - if user.EncryptedPassword == "" && user.InvitedAt != nil { + if !user.HasPassword() && user.InvitedAt != nil { // sign them up with temporary password, and require application // to present the user with a password set form password, err := password.Generate(64, 10, 0, false, true) @@ -322,7 +322,7 @@ func (a *API) signupVerify(r *http.Request, ctx context.Context, conn *storage.C err := conn.Transaction(func(tx *storage.Connection) error { var terr error - if user.EncryptedPassword == "" && user.InvitedAt != nil { + if !user.HasPassword() && user.InvitedAt != nil { if terr = user.UpdatePassword(tx, nil); terr != nil { return internalServerError("Error storing password").WithInternalError(terr) } diff --git a/internal/models/user.go b/internal/models/user.go index 721819eab..0d074562a 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -25,7 +25,7 @@ type User struct { Email storage.NullString `json:"email" db:"email"` IsSSOUser bool `json:"-" db:"is_sso_user"` - EncryptedPassword string `json:"-" db:"encrypted_password"` + EncryptedPassword *string `json:"-" db:"encrypted_password"` EmailConfirmedAt *time.Time `json:"email_confirmed_at,omitempty" db:"email_confirmed_at"` InvitedAt *time.Time `json:"invited_at,omitempty" db:"invited_at"` @@ -95,7 +95,7 @@ func NewUser(phone, email, password, aud string, userData map[string]interface{} Email: storage.NullString(strings.ToLower(email)), Phone: storage.NullString(phone), UserMetaData: userData, - EncryptedPassword: passwordHash, + EncryptedPassword: &passwordHash, } return user, nil } @@ -106,6 +106,16 @@ func (User) TableName() string { return tableName } +func (u *User) HasPassword() bool { + var pwd string + + if u.EncryptedPassword != nil { + pwd = *u.EncryptedPassword + } + + return pwd != "" +} + // BeforeSave is invoked before the user is saved to the database func (u *User) BeforeSave(tx *pop.Connection) error { if u.EmailConfirmedAt != nil && u.EmailConfirmedAt.IsZero() { @@ -285,7 +295,7 @@ func (u *User) SetPhone(tx *storage.Connection, phone string) error { func (u *User) SetPassword(ctx context.Context, password string, encrypt bool, encryptionKeyID, encryptionKey string) error { if password == "" { - u.EncryptedPassword = "" + u.EncryptedPassword = nil return nil } @@ -294,14 +304,15 @@ func (u *User) SetPassword(ctx context.Context, password string, encrypt bool, e return err } - u.EncryptedPassword = pw + u.EncryptedPassword = &pw if encrypt { es, err := crypto.NewEncryptedString(u.ID.String(), []byte(pw), encryptionKeyID, encryptionKey) if err != nil { return err } - u.EncryptedPassword = es.String() + encryptedPassword := es.String() + u.EncryptedPassword = &encryptedPassword } return nil @@ -341,9 +352,13 @@ func (u *User) UpdatePassword(tx *storage.Connection, sessionID *uuid.UUID) erro // Authenticate a user from a password func (u *User) Authenticate(ctx context.Context, password string, decryptionKeys map[string]string, encrypt bool, encryptionKeyID string) (bool, bool, error) { - hash := u.EncryptedPassword + if u.EncryptedPassword == nil { + return false, false, nil + } + + hash := *u.EncryptedPassword - es := crypto.ParseEncryptedString(u.EncryptedPassword) + es := crypto.ParseEncryptedString(hash) if es != nil { h, err := es.Decrypt(u.ID.String(), decryptionKeys) if err != nil { @@ -719,7 +734,7 @@ func (u *User) UpdateBannedUntil(tx *storage.Connection) error { func (u *User) RemoveUnconfirmedIdentities(tx *storage.Connection, identity *Identity) error { if identity.Provider != "email" && identity.Provider != "phone" { // user is unconfirmed so the password should be reset - u.EncryptedPassword = "" + u.EncryptedPassword = nil if terr := tx.UpdateOnly(u, "encrypted_password"); terr != nil { return terr } @@ -755,7 +770,7 @@ func (u *User) SoftDeleteUser(tx *storage.Connection) error { u.Phone = storage.NullString(obfuscatePhone(u, u.GetPhone())) u.EmailChange = obfuscateEmail(u, u.EmailChange) u.PhoneChange = obfuscatePhone(u, u.PhoneChange) - u.EncryptedPassword = "" + u.EncryptedPassword = nil u.ConfirmationToken = "" u.RecoveryToken = "" u.EmailChangeTokenCurrent = "" From 2cb97f080fa4695766985cc4792d09476534be68 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Fri, 28 Jun 2024 00:22:20 +0800 Subject: [PATCH 20/42] fix: upgrade golang-jwt to v5 (#1639) --- go.mod | 4 ++-- go.sum | 4 ++-- internal/api/admin_test.go | 2 +- internal/api/anonymous_test.go | 2 +- internal/api/audit_test.go | 4 ++-- internal/api/auth.go | 4 ++-- internal/api/auth_test.go | 16 ++++++++-------- internal/api/context.go | 2 +- internal/api/external.go | 8 ++++---- internal/api/external_apple_test.go | 4 ++-- internal/api/external_azure_test.go | 4 ++-- internal/api/external_bitbucket_test.go | 4 ++-- internal/api/external_discord_test.go | 4 ++-- internal/api/external_facebook_test.go | 4 ++-- internal/api/external_figma_test.go | 4 ++-- internal/api/external_fly_test.go | 4 ++-- internal/api/external_github_test.go | 4 ++-- internal/api/external_gitlab_test.go | 4 ++-- internal/api/external_google_test.go | 4 ++-- internal/api/external_kakao_test.go | 4 ++-- internal/api/external_keycloak_test.go | 4 ++-- internal/api/external_linkedin_test.go | 4 ++-- internal/api/external_notion_test.go | 4 ++-- internal/api/external_slack_oidc_test.go | 4 ++-- internal/api/external_twitch_test.go | 4 ++-- internal/api/external_workos_test.go | 8 ++++---- internal/api/external_zoom_test.go | 4 ++-- internal/api/helpers.go | 8 ++++++-- internal/api/identity.go | 3 ++- internal/api/invite_test.go | 4 ++-- internal/api/mail_test.go | 2 +- internal/api/middleware.go | 4 ++-- internal/api/middleware_test.go | 2 +- internal/api/provider/oidc.go | 10 +++++----- internal/api/sso_test.go | 2 +- internal/api/token.go | 16 ++++++++-------- internal/api/user.go | 3 ++- internal/hooks/auth_hooks.go | 6 +++--- 38 files changed, 94 insertions(+), 88 deletions(-) diff --git a/go.mod b/go.mod index 35ed75024..15c9a6651 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/gobuffalo/validate/v3 v3.3.3 // indirect github.com/gobwas/glob v0.2.3 github.com/gofrs/uuid v4.3.1+incompatible - github.com/golang-jwt/jwt v3.2.2+incompatible github.com/jackc/pgconn v1.14.3 github.com/jackc/pgerrcode v0.0.0-20201024163028-a0d42d470451 github.com/jackc/pgproto3/v2 v2.3.3 // indirect @@ -38,6 +37,7 @@ require ( github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect github.com/gobuffalo/nulls v0.4.2 // indirect + github.com/jackc/pgx/v4 v4.18.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect golang.org/x/mod v0.9.0 // indirect @@ -67,7 +67,7 @@ require ( github.com/fatih/structs v1.1.0 github.com/go-chi/chi/v5 v5.0.12 github.com/gobuffalo/pop/v6 v6.1.1 - github.com/jackc/pgx/v4 v4.18.2 + github.com/golang-jwt/jwt/v5 v5.2.1 github.com/standard-webhooks/standard-webhooks/libraries v0.0.0-20240303152453-e0e82adf1721 github.com/supabase/hibp v0.0.0-20231124125943-d225752ae869 github.com/supabase/mailme v0.2.0 diff --git a/go.sum b/go.sum index 0c289a276..66067a406 100644 --- a/go.sum +++ b/go.sum @@ -107,10 +107,10 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU= github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= diff --git a/internal/api/admin_test.go b/internal/api/admin_test.go index fa046045e..135616c1f 100644 --- a/internal/api/admin_test.go +++ b/internal/api/admin_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" diff --git a/internal/api/anonymous_test.go b/internal/api/anonymous_test.go index 92877cf19..6ab2bae04 100644 --- a/internal/api/anonymous_test.go +++ b/internal/api/anonymous_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/gofrs/uuid" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" diff --git a/internal/api/audit_test.go b/internal/api/audit_test.go index 8779ab678..c8e992ead 100644 --- a/internal/api/audit_test.go +++ b/internal/api/audit_test.go @@ -7,7 +7,7 @@ import ( "net/http/httptest" "testing" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -58,7 +58,7 @@ func (ts *AuditTestSuite) makeSuperAdmin(email string) string { token, _, err = ts.API.generateAccessToken(req, ts.API.db, u, &session.ID, models.PasswordGrant) require.NoError(ts.T(), err, "Error generating access token") - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.Parse(token, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/auth.go b/internal/api/auth.go index c167d8212..4e63715a1 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/gofrs/uuid" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/supabase/auth/internal/models" "github.com/supabase/auth/internal/storage" ) @@ -75,7 +75,7 @@ func (a *API) parseJWTClaims(bearer string, r *http.Request) (context.Context, e ctx := r.Context() config := a.config - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) token, err := p.ParseWithClaims(bearer, &AccessTokenClaims{}, func(token *jwt.Token) (interface{}, error) { return []byte(config.JWT.Secret), nil }) diff --git a/internal/api/auth_test.go b/internal/api/auth_test.go index 700d27af1..35a64be92 100644 --- a/internal/api/auth_test.go +++ b/internal/api/auth_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gofrs/uuid" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/supabase/auth/internal/conf" @@ -91,7 +91,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { { Desc: "Missing Subject Claim", UserJwtClaims: &AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: "", }, Role: "authenticated", @@ -102,7 +102,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { { Desc: "Valid Subject Claim", UserJwtClaims: &AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: u.ID.String(), }, Role: "authenticated", @@ -113,7 +113,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { { Desc: "Invalid Subject Claim", UserJwtClaims: &AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: "invalid-subject-claim", }, Role: "authenticated", @@ -124,7 +124,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { { Desc: "Empty Session ID Claim", UserJwtClaims: &AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: u.ID.String(), }, Role: "authenticated", @@ -136,7 +136,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { { Desc: "Invalid Session ID Claim", UserJwtClaims: &AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: u.ID.String(), }, Role: "authenticated", @@ -148,7 +148,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { { Desc: "Valid Session ID Claim", UserJwtClaims: &AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: u.ID.String(), }, Role: "authenticated", @@ -161,7 +161,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { { Desc: "Session ID doesn't exist", UserJwtClaims: &AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: u.ID.String(), }, Role: "authenticated", diff --git a/internal/api/context.go b/internal/api/context.go index b357299a6..3047f3dd6 100644 --- a/internal/api/context.go +++ b/internal/api/context.go @@ -4,7 +4,7 @@ import ( "context" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/supabase/auth/internal/models" ) diff --git a/internal/api/external.go b/internal/api/external.go index a8048fb26..ef6032d9a 100644 --- a/internal/api/external.go +++ b/internal/api/external.go @@ -12,7 +12,7 @@ import ( "github.com/fatih/structs" "github.com/gofrs/uuid" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/sirupsen/logrus" "github.com/supabase/auth/internal/api/provider" "github.com/supabase/auth/internal/models" @@ -89,8 +89,8 @@ func (a *API) GetExternalProviderRedirectURL(w http.ResponseWriter, r *http.Requ claims := ExternalProviderClaims{ AuthMicroserviceClaims: AuthMicroserviceClaims{ - StandardClaims: jwt.StandardClaims{ - ExpiresAt: time.Now().Add(5 * time.Minute).Unix(), + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)), }, SiteURL: config.SiteURL, InstanceID: uuid.Nil.String(), @@ -481,7 +481,7 @@ func (a *API) processInvite(r *http.Request, tx *storage.Connection, userData *p func (a *API) loadExternalState(ctx context.Context, state string) (context.Context, error) { config := a.config claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err := p.ParseWithClaims(state, &claims, func(token *jwt.Token) (interface{}, error) { return []byte(config.JWT.Secret), nil }) diff --git a/internal/api/external_apple_test.go b/internal/api/external_apple_test.go index 0413e2bda..5a0b4970c 100644 --- a/internal/api/external_apple_test.go +++ b/internal/api/external_apple_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) func (ts *ExternalTestSuite) TestSignupExternalApple() { @@ -22,7 +22,7 @@ func (ts *ExternalTestSuite) TestSignupExternalApple() { ts.Equal("email name", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_azure_test.go b/internal/api/external_azure_test.go index e940ba9f6..aac124c78 100644 --- a/internal/api/external_azure_test.go +++ b/internal/api/external_azure_test.go @@ -15,7 +15,7 @@ import ( "time" "github.com/coreos/go-oidc/v3/oidc" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/supabase/auth/internal/api/provider" ) @@ -116,7 +116,7 @@ func (ts *ExternalTestSuite) TestSignupExternalAzure() { ts.Equal("openid", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_bitbucket_test.go b/internal/api/external_bitbucket_test.go index fad66456f..66b3bd4df 100644 --- a/internal/api/external_bitbucket_test.go +++ b/internal/api/external_bitbucket_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -27,7 +27,7 @@ func (ts *ExternalTestSuite) TestSignupExternalBitbucket() { ts.Equal("account email", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_discord_test.go b/internal/api/external_discord_test.go index eeae84e13..7b6be8d34 100644 --- a/internal/api/external_discord_test.go +++ b/internal/api/external_discord_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -29,7 +29,7 @@ func (ts *ExternalTestSuite) TestSignupExternalDiscord() { ts.Equal("email identify", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_facebook_test.go b/internal/api/external_facebook_test.go index c484218f8..c1864bb9c 100644 --- a/internal/api/external_facebook_test.go +++ b/internal/api/external_facebook_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -29,7 +29,7 @@ func (ts *ExternalTestSuite) TestSignupExternalFacebook() { ts.Equal("email", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_figma_test.go b/internal/api/external_figma_test.go index 45e403759..ccd59b1ff 100644 --- a/internal/api/external_figma_test.go +++ b/internal/api/external_figma_test.go @@ -11,7 +11,7 @@ import ( "net/url" "time" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/supabase/auth/internal/models" ) @@ -30,7 +30,7 @@ func (ts *ExternalTestSuite) TestSignupExternalFigma() { ts.Equal("file_read", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_fly_test.go b/internal/api/external_fly_test.go index b8ddf1a6d..cf357c97b 100644 --- a/internal/api/external_fly_test.go +++ b/internal/api/external_fly_test.go @@ -11,7 +11,7 @@ import ( "net/url" "time" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/supabase/auth/internal/models" ) @@ -30,7 +30,7 @@ func (ts *ExternalTestSuite) TestSignupExternalFly() { ts.Equal("read", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_github_test.go b/internal/api/external_github_test.go index ca6df9543..7b9d31e89 100644 --- a/internal/api/external_github_test.go +++ b/internal/api/external_github_test.go @@ -11,7 +11,7 @@ import ( "net/url" "time" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/supabase/auth/internal/models" ) @@ -30,7 +30,7 @@ func (ts *ExternalTestSuite) TestSignupExternalGithub() { ts.Equal("user:email", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_gitlab_test.go b/internal/api/external_gitlab_test.go index 1a32655cf..5a14a0a2b 100644 --- a/internal/api/external_gitlab_test.go +++ b/internal/api/external_gitlab_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -29,7 +29,7 @@ func (ts *ExternalTestSuite) TestSignupExternalGitlab() { ts.Equal("read_user", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_google_test.go b/internal/api/external_google_test.go index 8458be5d0..7b3b6d156 100644 --- a/internal/api/external_google_test.go +++ b/internal/api/external_google_test.go @@ -7,7 +7,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/supabase/auth/internal/api/provider" ) @@ -34,7 +34,7 @@ func (ts *ExternalTestSuite) TestSignupExternalGoogle() { ts.Equal("email profile", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_kakao_test.go b/internal/api/external_kakao_test.go index cd2bd2b29..729f723a7 100644 --- a/internal/api/external_kakao_test.go +++ b/internal/api/external_kakao_test.go @@ -8,7 +8,7 @@ import ( "net/url" "time" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/supabase/auth/internal/api/provider" "github.com/supabase/auth/internal/models" @@ -27,7 +27,7 @@ func (ts *ExternalTestSuite) TestSignupExternalKakao() { ts.Equal("code", q.Get("response_type")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_keycloak_test.go b/internal/api/external_keycloak_test.go index 2562664a4..a0952eaac 100644 --- a/internal/api/external_keycloak_test.go +++ b/internal/api/external_keycloak_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -28,7 +28,7 @@ func (ts *ExternalTestSuite) TestSignupExternalKeycloak() { ts.Equal("profile email", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_linkedin_test.go b/internal/api/external_linkedin_test.go index 27449dd3a..fe49932e5 100644 --- a/internal/api/external_linkedin_test.go +++ b/internal/api/external_linkedin_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -30,7 +30,7 @@ func (ts *ExternalTestSuite) TestSignupExternalLinkedin() { ts.Equal("r_emailaddress r_liteprofile", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_notion_test.go b/internal/api/external_notion_test.go index 1680b14c2..268e4492b 100644 --- a/internal/api/external_notion_test.go +++ b/internal/api/external_notion_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -28,7 +28,7 @@ func (ts *ExternalTestSuite) TestSignupExternalNotion() { ts.Equal("code", q.Get("response_type")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_slack_oidc_test.go b/internal/api/external_slack_oidc_test.go index 9090581d0..acd2e784d 100644 --- a/internal/api/external_slack_oidc_test.go +++ b/internal/api/external_slack_oidc_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) func (ts *ExternalTestSuite) TestSignupExternalSlackOIDC() { @@ -22,7 +22,7 @@ func (ts *ExternalTestSuite) TestSignupExternalSlackOIDC() { ts.Equal("profile email openid", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_twitch_test.go b/internal/api/external_twitch_test.go index 775c44ef8..694a5ff68 100644 --- a/internal/api/external_twitch_test.go +++ b/internal/api/external_twitch_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -28,7 +28,7 @@ func (ts *ExternalTestSuite) TestSignupExternalTwitch() { ts.Equal("user:read:email", q.Get("scope")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_workos_test.go b/internal/api/external_workos_test.go index 05d3175cb..eedd5b00e 100644 --- a/internal/api/external_workos_test.go +++ b/internal/api/external_workos_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -31,7 +31,7 @@ func (ts *ExternalTestSuite) TestSignupExternalWorkOSWithConnection() { ts.Equal(connection, q.Get("connection")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) @@ -57,7 +57,7 @@ func (ts *ExternalTestSuite) TestSignupExternalWorkOSWithOrganization() { ts.Equal(organization, q.Get("organization")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) @@ -83,7 +83,7 @@ func (ts *ExternalTestSuite) TestSignupExternalWorkOSWithProvider() { ts.Equal(provider, q.Get("provider")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/external_zoom_test.go b/internal/api/external_zoom_test.go index cac2c647f..ea3f15c4d 100644 --- a/internal/api/external_zoom_test.go +++ b/internal/api/external_zoom_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "net/url" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) const ( @@ -28,7 +28,7 @@ func (ts *ExternalTestSuite) TestSignupExternalZoom() { ts.Equal("code", q.Get("response_type")) claims := ExternalProviderClaims{} - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.ParseWithClaims(q.Get("state"), &claims, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/helpers.go b/internal/api/helpers.go index bcdce1416..58ac82d08 100644 --- a/internal/api/helpers.go +++ b/internal/api/helpers.go @@ -36,8 +36,12 @@ func (a *API) requestAud(ctx context.Context, r *http.Request) string { // Then check the token claims := getClaims(ctx) - if claims != nil && claims.Audience != "" { - return claims.Audience + + if claims != nil { + aud, _ := claims.GetAudience() + if len(aud) != 0 { + return aud[0] + } } // Finally, return the default if none of the above methods are successful diff --git a/internal/api/identity.go b/internal/api/identity.go index 7acf7c25c..69d3f854f 100644 --- a/internal/api/identity.go +++ b/internal/api/identity.go @@ -27,7 +27,8 @@ func (a *API) DeleteIdentity(w http.ResponseWriter, r *http.Request) error { } aud := a.requestAud(ctx, r) - if aud != claims.Audience { + audienceFromClaims, _ := claims.GetAudience() + if len(audienceFromClaims) == 0 || aud != audienceFromClaims[0] { return forbiddenError(ErrorCodeUnexpectedAudience, "Token audience doesn't match request audience") } diff --git a/internal/api/invite_test.go b/internal/api/invite_test.go index 864463d10..ff0baca6a 100644 --- a/internal/api/invite_test.go +++ b/internal/api/invite_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -70,7 +70,7 @@ func (ts *InviteTestSuite) makeSuperAdmin(email string) string { require.NoError(ts.T(), err, "Error generating access token") - p := jwt.Parser{ValidMethods: []string{jwt.SigningMethodHS256.Name}} + p := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) _, err = p.Parse(token, func(token *jwt.Token) (interface{}, error) { return []byte(ts.Config.JWT.Secret), nil }) diff --git a/internal/api/mail_test.go b/internal/api/mail_test.go index 6063a9a6b..90608a13a 100644 --- a/internal/api/mail_test.go +++ b/internal/api/mail_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/gobwas/glob" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/supabase/auth/internal/conf" diff --git a/internal/api/middleware.go b/internal/api/middleware.go index d4e3068eb..8caa5d885 100644 --- a/internal/api/middleware.go +++ b/internal/api/middleware.go @@ -20,13 +20,13 @@ import ( "github.com/didip/tollbooth/v5" "github.com/didip/tollbooth/v5/limiter" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" ) type FunctionHooks map[string][]string type AuthMicroserviceClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims SiteURL string `json:"site_url"` InstanceID string `json:"id"` FunctionHooks FunctionHooks `json:"function_hooks"` diff --git a/internal/api/middleware_test.go b/internal/api/middleware_test.go index 4d0e327f3..eb8c5da3b 100644 --- a/internal/api/middleware_test.go +++ b/internal/api/middleware_test.go @@ -13,7 +13,7 @@ import ( "github.com/didip/tollbooth/v5" "github.com/didip/tollbooth/v5/limiter" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" diff --git a/internal/api/provider/oidc.go b/internal/api/provider/oidc.go index 01f7ab7e6..51deaeff3 100644 --- a/internal/api/provider/oidc.go +++ b/internal/api/provider/oidc.go @@ -8,7 +8,7 @@ import ( "time" "github.com/coreos/go-oidc/v3/oidc" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" ) type ParseIDTokenOptions struct { @@ -120,7 +120,7 @@ func parseGoogleIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData, } type AppleIDTokenClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims Email string `json:"email"` @@ -165,7 +165,7 @@ func parseAppleIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData, e } type LinkedinIDTokenClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims Email string `json:"email"` EmailVerified string `json:"email_verified"` @@ -210,7 +210,7 @@ func parseLinkedinIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData } type AzureIDTokenClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims Email string `json:"email"` Name string `json:"name"` @@ -315,7 +315,7 @@ func parseAzureIDToken(token *oidc.IDToken) (*oidc.IDToken, *UserProvidedData, e } type KakaoIDTokenClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims Email string `json:"email"` Nickname string `json:"nickname"` diff --git a/internal/api/sso_test.go b/internal/api/sso_test.go index 5fc46b2d0..bae1bebf3 100644 --- a/internal/api/sso_test.go +++ b/internal/api/sso_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - jwt "github.com/golang-jwt/jwt" + jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/supabase/auth/internal/conf" diff --git a/internal/api/token.go b/internal/api/token.go index 11af2883f..0d03d4fd4 100644 --- a/internal/api/token.go +++ b/internal/api/token.go @@ -11,7 +11,7 @@ import ( "fmt" "github.com/gofrs/uuid" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/xeipuuv/gojsonschema" "github.com/supabase/auth/internal/conf" @@ -24,7 +24,7 @@ import ( // AccessTokenClaims is a struct thats used for JWT claims type AccessTokenClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims Email string `json:"email"` Phone string `json:"phone"` AppMetaData map[string]interface{} `json:"app_metadata"` @@ -324,14 +324,14 @@ func (a *API) generateAccessToken(r *http.Request, tx *storage.Connection, user } issuedAt := time.Now().UTC() - expiresAt := issuedAt.Add(time.Second * time.Duration(config.JWT.Exp)).Unix() + expiresAt := issuedAt.Add(time.Second * time.Duration(config.JWT.Exp)) claims := &hooks.AccessTokenClaims{ - StandardClaims: jwt.StandardClaims{ + RegisteredClaims: jwt.RegisteredClaims{ Subject: user.ID.String(), - Audience: user.Aud, - IssuedAt: issuedAt.Unix(), - ExpiresAt: expiresAt, + Audience: []string{user.Aud}, + IssuedAt: jwt.NewNumericDate(issuedAt), + ExpiresAt: jwt.NewNumericDate(expiresAt), Issuer: config.JWT.Issuer, }, Email: user.GetEmail(), @@ -380,7 +380,7 @@ func (a *API) generateAccessToken(r *http.Request, tx *storage.Connection, user return "", 0, err } - return signed, expiresAt, nil + return signed, expiresAt.Unix(), nil } func (a *API) issueRefreshToken(r *http.Request, conn *storage.Connection, user *models.User, authenticationMethod models.AuthenticationMethod, grantParams models.GrantParams) (*AccessTokenResponse, error) { diff --git a/internal/api/user.go b/internal/api/user.go index 6fd8d34e5..960322c94 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -66,7 +66,8 @@ func (a *API) UserGet(w http.ResponseWriter, r *http.Request) error { } aud := a.requestAud(ctx, r) - if aud != claims.Audience { + audienceFromClaims, _ := claims.GetAudience() + if len(audienceFromClaims) == 0 || aud != audienceFromClaims[0] { return badRequestError(ErrorCodeValidationFailed, "Token audience doesn't match request audience") } diff --git a/internal/hooks/auth_hooks.go b/internal/hooks/auth_hooks.go index 711334429..c11b91b5e 100644 --- a/internal/hooks/auth_hooks.go +++ b/internal/hooks/auth_hooks.go @@ -2,7 +2,7 @@ package hooks import ( "github.com/gofrs/uuid" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/supabase/auth/internal/mailer" "github.com/supabase/auth/internal/models" ) @@ -43,7 +43,7 @@ const MinimumViableTokenSchema = `{ "type": "object", "properties": { "aud": { - "type": "string" + "type": "array" }, "exp": { "type": "integer" @@ -98,7 +98,7 @@ const MinimumViableTokenSchema = `{ // AccessTokenClaims is a struct thats used for JWT claims type AccessTokenClaims struct { - jwt.StandardClaims + jwt.RegisteredClaims Email string `json:"email"` Phone string `json:"phone"` AppMetaData map[string]interface{} `json:"app_metadata"` From 97b8f1ba3f615d192c2952977415b96f2576bf46 Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Tue, 2 Jul 2024 22:35:49 +0200 Subject: [PATCH 21/42] fix: return proper error message when invalid email_change token is passed in (#1643) ## What kind of change does this PR introduce? Currently, when `SECURE_EMAIL_CHANGE` is enabled and a dev invokes `verifyOtp({type:'email_change', token:'', email:''})` an internalServerError and panic is returned. After this change a 403 is returned as expected. This is because when an invalid `email_change` OTP is submitted [an access on ott.UserID (which is nil) is made ](https://github.com/supabase/auth/compare/j0/prevent_panic_on_email_change?expand=1#diff-55a81f8dc53ee75ff592eec9c5e1fcb665f653d7593de7e9c8b8dadf7159fe83L258) --- internal/api/verify_test.go | 16 ++++++++++++++++ internal/models/one_time_token.go | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/internal/api/verify_test.go b/internal/api/verify_test.go index 8d818b43a..73ef6f768 100644 --- a/internal/api/verify_test.go +++ b/internal/api/verify_test.go @@ -318,9 +318,14 @@ func (ts *VerifyTestSuite) TestInvalidOtp() { u.PhoneChange = "22222222" u.PhoneChangeToken = "123456" u.PhoneChangeSentAt = &sentTime + u.EmailChange = "test@gmail.com" + u.EmailChangeTokenNew = "123456" + u.EmailChangeTokenCurrent = "123456" require.NoError(ts.T(), ts.API.db.Update(u)) require.NoError(ts.T(), models.CreateOneTimeToken(ts.API.db, u.ID, u.GetEmail(), u.ConfirmationToken, models.ConfirmationToken)) require.NoError(ts.T(), models.CreateOneTimeToken(ts.API.db, u.ID, u.PhoneChange, u.PhoneChangeToken, models.PhoneChangeToken)) + require.NoError(ts.T(), models.CreateOneTimeToken(ts.API.db, u.ID, u.GetEmail(), u.EmailChangeTokenCurrent, models.EmailChangeTokenCurrent)) + require.NoError(ts.T(), models.CreateOneTimeToken(ts.API.db, u.ID, u.EmailChange, u.EmailChangeTokenNew, models.EmailChangeTokenNew)) type ResponseBody struct { Code int `json:"code"` @@ -378,6 +383,16 @@ func (ts *VerifyTestSuite) TestInvalidOtp() { }, expected: expectedResponse, }, + { + desc: "Invalid Email Change", + sentTime: time.Now(), + body: map[string]interface{}{ + "type": mail.EmailChangeVerification, + "token": "invalid_otp", + "email": u.GetEmail(), + }, + expected: expectedResponse, + }, } for _, caseItem := range cases { @@ -814,6 +829,7 @@ func (ts *VerifyTestSuite) TestVerifyBannedUser() { } func (ts *VerifyTestSuite) TestVerifyValidOtp() { + ts.Config.Mailer.SecureEmailChangeEnabled = true u, err := models.FindUserByEmailAndAudience(ts.API.db, "test@example.com", ts.Config.JWT.Aud) require.NoError(ts.T(), err) u.EmailChange = "new@example.com" diff --git a/internal/models/one_time_token.go b/internal/models/one_time_token.go index c5a204902..3077647a5 100644 --- a/internal/models/one_time_token.go +++ b/internal/models/one_time_token.go @@ -228,6 +228,9 @@ func FindUserByEmailChangeCurrentAndAudience(tx *storage.Connection, email, toke return nil, err } } + if ott == nil { + return nil, err + } user, err := FindUserByID(tx, ott.UserID) if err != nil { @@ -254,6 +257,9 @@ func FindUserByEmailChangeNewAndAudience(tx *storage.Connection, email, token, a return nil, err } } + if ott == nil { + return nil, err + } user, err := FindUserByID(tx, ott.UserID) if err != nil { From 3f70d9d8974d0e9c437c51e1312ad17ce9056ec9 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 3 Jul 2024 12:12:33 -0700 Subject: [PATCH 22/42] fix: invited users should have a temporary password generated (#1644) ## What kind of change does this PR introduce? * Fixes a bug in the boolean condition where an invited user fails to have their password updated to a temporary password on invite confirmation --- internal/api/verify.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/api/verify.go b/internal/api/verify.go index 1040ce7c6..c0b78d680 100644 --- a/internal/api/verify.go +++ b/internal/api/verify.go @@ -306,6 +306,7 @@ func (a *API) verifyPost(w http.ResponseWriter, r *http.Request, params *VerifyP func (a *API) signupVerify(r *http.Request, ctx context.Context, conn *storage.Connection, user *models.User) (*models.User, error) { config := a.config + shouldUpdatePassword := false if !user.HasPassword() && user.InvitedAt != nil { // sign them up with temporary password, and require application // to present the user with a password set form @@ -318,11 +319,12 @@ func (a *API) signupVerify(r *http.Request, ctx context.Context, conn *storage.C if err := user.SetPassword(ctx, password, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID, config.Security.DBEncryption.EncryptionKey); err != nil { return nil, err } + shouldUpdatePassword = true } err := conn.Transaction(func(tx *storage.Connection) error { var terr error - if !user.HasPassword() && user.InvitedAt != nil { + if shouldUpdatePassword { if terr = user.UpdatePassword(tx, nil); terr != nil { return internalServerError("Error storing password").WithInternalError(terr) } From 20d59f10b601577683d05bcd7d2128ff4bc462a0 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 3 Jul 2024 12:13:22 -0700 Subject: [PATCH 23/42] feat: add `password_hash` and `id` fields to admin create user (#1641) ## What kind of change does this PR introduce? * Add a `password_hash` field to admin create user, which allows an admin to create a user with a given password hash (argon2 or bcrypt) * Add an `id` field to admin create user, which allows an admin to create a user with a custom id * To prevent someone from creating a bunch of users with a high bcrypt hashing cost, we opt to rehash the password with the default cost (10) on subsequent sign-in. ## What is the current behavior? * Only plaintext passwords are allowed, which will subsequently be hashed internally ## What is the new behavior? Example request using the bcrypt hash of "test": ```bash $ curl -X POST 'http://localhost:9999/admin/users' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"email": "foo@example.com", "password_hash": "$2y$10$SXEz2HeT8PUIGQXo9yeUIem8KzNxgG0d7o/.eGj2rj8KbRgAuRVlq"}' ``` Example request using a custom id: ```bash $ curl -X POST 'http://localhost:9999/admin/users' \ -H 'Authorization: Bearer ' \ -H 'Content-Type: application/json' \ -d '{"id": "2a8813c2-bda7-47f0-94a6-49fcfdf61a70", "email": "foo@example.com"}' ``` Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- internal/api/admin.go | 65 ++++++++++++++------ internal/api/admin_test.go | 112 ++++++++++++++++++++++++++++++++--- internal/api/token.go | 2 +- internal/api/user.go | 2 +- internal/api/user_test.go | 6 +- internal/crypto/password.go | 73 +++++++++++++++-------- internal/models/user.go | 44 +++++++++++++- internal/models/user_test.go | 92 ++++++++++++++++++++++++++++ 8 files changed, 340 insertions(+), 56 deletions(-) diff --git a/internal/api/admin.go b/internal/api/admin.go index 1cda8e264..ecd9c2053 100644 --- a/internal/api/admin.go +++ b/internal/api/admin.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "net/http" - "strings" "time" "github.com/fatih/structs" @@ -18,11 +17,13 @@ import ( ) type AdminUserParams struct { + Id string `json:"id"` Aud string `json:"aud"` Role string `json:"role"` Email string `json:"email"` Phone string `json:"phone"` Password *string `json:"password"` + PasswordHash string `json:"password_hash"` EmailConfirm bool `json:"email_confirm"` PhoneConfirm bool `json:"phone_confirm"` UserMetaData map[string]interface{} `json:"user_metadata"` @@ -156,6 +157,7 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { } } + var banDuration *time.Duration if params.BanDuration != "" { duration := time.Duration(0) if params.BanDuration != "none" { @@ -164,9 +166,7 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { return badRequestError(ErrorCodeValidationFailed, "invalid format for ban duration: %v", err) } } - if terr := user.Ban(a.db, duration); terr != nil { - return terr - } + banDuration = &duration } if params.Password != nil { @@ -291,6 +291,12 @@ func (a *API) adminUserUpdate(w http.ResponseWriter, r *http.Request) error { } } + if banDuration != nil { + if terr := user.Ban(tx, *banDuration); terr != nil { + return terr + } + } + if terr := models.NewAuditLogEntry(r, tx, adminUser, models.UserModifiedAction, "", map[string]interface{}{ "user_id": user.ID, "user_email": user.Email, @@ -356,7 +362,11 @@ func (a *API) adminUserCreate(w http.ResponseWriter, r *http.Request) error { providers = append(providers, "phone") } - if params.Password == nil || *params.Password == "" { + if params.Password != nil && params.PasswordHash != "" { + return badRequestError(ErrorCodeValidationFailed, "Only a password or a password hash should be provided") + } + + if (params.Password == nil || *params.Password == "") && params.PasswordHash == "" { password, err := password.Generate(64, 10, 0, false, true) if err != nil { return internalServerError("Error generating password").WithInternalError(err) @@ -364,11 +374,28 @@ func (a *API) adminUserCreate(w http.ResponseWriter, r *http.Request) error { params.Password = &password } - user, err := models.NewUser(params.Phone, params.Email, *params.Password, aud, params.UserMetaData) + var user *models.User + if params.PasswordHash != "" { + user, err = models.NewUserWithPasswordHash(params.Phone, params.Email, params.PasswordHash, aud, params.UserMetaData) + } else { + user, err = models.NewUser(params.Phone, params.Email, *params.Password, aud, params.UserMetaData) + } + if err != nil { return internalServerError("Error creating user").WithInternalError(err) } + if params.Id != "" { + customId, err := uuid.FromString(params.Id) + if err != nil { + return badRequestError(ErrorCodeValidationFailed, "ID must conform to the uuid v4 format") + } + if customId == uuid.Nil { + return badRequestError(ErrorCodeValidationFailed, "ID cannot be a nil uuid") + } + user.ID = customId + } + user.AppMetaData = map[string]interface{}{ // TODO: Deprecate "provider" field // default to the first provider in the providers slice @@ -376,6 +403,18 @@ func (a *API) adminUserCreate(w http.ResponseWriter, r *http.Request) error { "providers": providers, } + var banDuration *time.Duration + if params.BanDuration != "" { + duration := time.Duration(0) + if params.BanDuration != "none" { + duration, err = time.ParseDuration(params.BanDuration) + if err != nil { + return badRequestError(ErrorCodeValidationFailed, "invalid format for ban duration: %v", err) + } + } + banDuration = &duration + } + err = db.Transaction(func(tx *storage.Connection) error { if terr := tx.Create(user); terr != nil { return terr @@ -442,15 +481,8 @@ func (a *API) adminUserCreate(w http.ResponseWriter, r *http.Request) error { } } - if params.BanDuration != "" { - duration := time.Duration(0) - if params.BanDuration != "none" { - duration, err = time.ParseDuration(params.BanDuration) - if err != nil { - return badRequestError(ErrorCodeValidationFailed, "invalid format for ban duration: %v", err) - } - } - if terr := user.Ban(a.db, duration); terr != nil { + if banDuration != nil { + if terr := user.Ban(tx, *banDuration); terr != nil { return terr } } @@ -459,9 +491,6 @@ func (a *API) adminUserCreate(w http.ResponseWriter, r *http.Request) error { }) if err != nil { - if strings.Contains("invalid format for ban duration", err.Error()) { - return err - } return internalServerError("Database error creating new user").WithInternalError(err) } diff --git a/internal/api/admin_test.go b/internal/api/admin_test.go index 135616c1f..e1b2c0328 100644 --- a/internal/api/admin_test.go +++ b/internal/api/admin_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/gofrs/uuid" jwt "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -244,6 +245,7 @@ func (ts *AdminTestSuite) TestAdminUserCreate() { "isAuthenticated": true, "provider": "phone", "providers": []string{"phone"}, + "password": "test1", }, }, { @@ -259,6 +261,7 @@ func (ts *AdminTestSuite) TestAdminUserCreate() { "isAuthenticated": true, "provider": "email", "providers": []string{"email", "phone"}, + "password": "test1", }, }, { @@ -288,6 +291,7 @@ func (ts *AdminTestSuite) TestAdminUserCreate() { "isAuthenticated": false, "provider": "email", "providers": []string{"email"}, + "password": "", }, }, { @@ -304,6 +308,39 @@ func (ts *AdminTestSuite) TestAdminUserCreate() { "isAuthenticated": true, "provider": "email", "providers": []string{"email"}, + "password": "test1", + }, + }, + { + desc: "With password hash", + params: map[string]interface{}{ + "email": "test5@example.com", + "password_hash": "$2y$10$SXEz2HeT8PUIGQXo9yeUIem8KzNxgG0d7o/.eGj2rj8KbRgAuRVlq", + }, + expected: map[string]interface{}{ + "email": "test5@example.com", + "phone": "", + "isAuthenticated": true, + "provider": "email", + "providers": []string{"email"}, + "password": "test", + }, + }, + { + desc: "With custom id", + params: map[string]interface{}{ + "id": "fc56ab41-2010-4870-a9b9-767c1dc573fb", + "email": "test6@example.com", + "password": "test", + }, + expected: map[string]interface{}{ + "id": "fc56ab41-2010-4870-a9b9-767c1dc573fb", + "email": "test6@example.com", + "phone": "", + "isAuthenticated": true, + "provider": "email", + "providers": []string{"email"}, + "password": "test", }, }, } @@ -345,15 +382,18 @@ func (ts *AdminTestSuite) TestAdminUserCreate() { } } - var expectedPassword string - if _, ok := c.params["password"]; ok { - expectedPassword = fmt.Sprintf("%v", c.params["password"]) + if _, ok := c.expected["password"]; ok { + expectedPassword := fmt.Sprintf("%v", c.expected["password"]) + isAuthenticated, _, err := u.Authenticate(context.Background(), ts.API.db, expectedPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) + require.NoError(ts.T(), err) + require.Equal(ts.T(), c.expected["isAuthenticated"], isAuthenticated) } - isAuthenticated, _, err := u.Authenticate(context.Background(), expectedPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) - require.NoError(ts.T(), err) - - assert.Equal(ts.T(), c.expected["isAuthenticated"], isAuthenticated) + if id, ok := c.expected["id"]; ok { + uid, err := uuid.FromString(id.(string)) + require.NoError(ts.T(), err) + require.Equal(ts.T(), uid, data.ID) + } // remove created user after each case require.NoError(ts.T(), ts.API.db.Destroy(u)) @@ -820,5 +860,63 @@ func (ts *AdminTestSuite) TestAdminUserUpdateFactor() { require.Equal(ts.T(), c.ExpectedCode, w.Code) }) } +} + +func (ts *AdminTestSuite) TestAdminUserCreateValidationErrors() { + cases := []struct { + desc string + params map[string]interface{} + }{ + { + desc: "create user without email and phone", + params: map[string]interface{}{ + "password": "test_password", + }, + }, + { + desc: "create user with password and password hash", + params: map[string]interface{}{ + "email": "test@example.com", + "password": "test_password", + "password_hash": "$2y$10$Tk6yEdmTbb/eQ/haDMaCsuCsmtPVprjHMcij1RqiJdLGPDXnL3L1a", + }, + }, + { + desc: "invalid ban duration", + params: map[string]interface{}{ + "email": "test@example.com", + "ban_duration": "never", + }, + }, + { + desc: "custom id is nil", + params: map[string]interface{}{ + "id": "00000000-0000-0000-0000-000000000000", + "email": "test@example.com", + }, + }, + { + desc: "bad id format", + params: map[string]interface{}{ + "id": "bad_uuid_format", + "email": "test@example.com", + }, + }, + } + for _, c := range cases { + ts.Run(c.desc, func() { + var buffer bytes.Buffer + require.NoError(ts.T(), json.NewEncoder(&buffer).Encode(c.params)) + req := httptest.NewRequest(http.MethodPost, "/admin/users", &buffer) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", ts.token)) + w := httptest.NewRecorder() + ts.API.handler.ServeHTTP(w, req) + require.Equal(ts.T(), http.StatusBadRequest, w.Code, w) + data := map[string]interface{}{} + require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data)) + require.Equal(ts.T(), data["error_code"], ErrorCodeValidationFailed) + }) + + } } diff --git a/internal/api/token.go b/internal/api/token.go index 0d03d4fd4..22c42e9b1 100644 --- a/internal/api/token.go +++ b/internal/api/token.go @@ -145,7 +145,7 @@ func (a *API) ResourceOwnerPasswordGrant(ctx context.Context, w http.ResponseWri return oauthError("invalid_grant", InvalidLoginMessage) } - isValidPassword, shouldReEncrypt, err := user.Authenticate(ctx, params.Password, config.Security.DBEncryption.DecryptionKeys, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID) + isValidPassword, shouldReEncrypt, err := user.Authenticate(ctx, db, params.Password, config.Security.DBEncryption.DecryptionKeys, config.Security.DBEncryption.Encrypt, config.Security.DBEncryption.EncryptionKeyID) if err != nil { return err } diff --git a/internal/api/user.go b/internal/api/user.go index 960322c94..4cb1fc1d9 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -157,7 +157,7 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { isSamePassword := false if user.HasPassword() { - auth, _, err := user.Authenticate(ctx, password, config.Security.DBEncryption.DecryptionKeys, false, "") + auth, _, err := user.Authenticate(ctx, db, password, config.Security.DBEncryption.DecryptionKeys, false, "") if err != nil { return err } diff --git a/internal/api/user_test.go b/internal/api/user_test.go index 8272bb87e..af9cfec37 100644 --- a/internal/api/user_test.go +++ b/internal/api/user_test.go @@ -310,7 +310,7 @@ func (ts *UserTestSuite) TestUserUpdatePassword() { u, err = models.FindUserByEmailAndAudience(ts.API.db, "test@example.com", ts.Config.JWT.Aud) require.NoError(ts.T(), err) - isAuthenticated, _, err := u.Authenticate(context.Background(), c.newPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) + isAuthenticated, _, err := u.Authenticate(context.Background(), ts.API.db, c.newPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) require.NoError(ts.T(), err) require.Equal(ts.T(), c.expected.isAuthenticated, isAuthenticated) @@ -372,7 +372,7 @@ func (ts *UserTestSuite) TestUserUpdatePasswordNoReauthenticationRequired() { u, err = models.FindUserByEmailAndAudience(ts.API.db, "test@example.com", ts.Config.JWT.Aud) require.NoError(ts.T(), err) - isAuthenticated, _, err := u.Authenticate(context.Background(), c.newPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) + isAuthenticated, _, err := u.Authenticate(context.Background(), ts.API.db, c.newPassword, ts.API.config.Security.DBEncryption.DecryptionKeys, ts.API.config.Security.DBEncryption.Encrypt, ts.API.config.Security.DBEncryption.EncryptionKeyID) require.NoError(ts.T(), err) require.Equal(ts.T(), c.expected.isAuthenticated, isAuthenticated) @@ -430,7 +430,7 @@ func (ts *UserTestSuite) TestUserUpdatePasswordReauthentication() { u, err = models.FindUserByEmailAndAudience(ts.API.db, "test@example.com", ts.Config.JWT.Aud) require.NoError(ts.T(), err) - isAuthenticated, _, err := u.Authenticate(context.Background(), "newpass", ts.Config.Security.DBEncryption.DecryptionKeys, ts.Config.Security.DBEncryption.Encrypt, ts.Config.Security.DBEncryption.EncryptionKeyID) + isAuthenticated, _, err := u.Authenticate(context.Background(), ts.API.db, "newpass", ts.Config.Security.DBEncryption.DecryptionKeys, ts.Config.Security.DBEncryption.Encrypt, ts.Config.Security.DBEncryption.EncryptionKeyID) require.NoError(ts.T(), err) require.True(ts.T(), isAuthenticated) diff --git a/internal/crypto/password.go b/internal/crypto/password.go index 554daccaa..dca101450 100644 --- a/internal/crypto/password.go +++ b/internal/crypto/password.go @@ -32,6 +32,8 @@ const ( // BCrypt hashed passwords have a 72 character limit MaxPasswordLength = 72 + + Argon2Prefix = "$argon2" ) // PasswordHashCost is the current pasword hashing cost @@ -54,11 +56,23 @@ var ErrArgon2MismatchedHashAndPassword = errors.New("crypto: argon2 hash and pas // argon2HashRegexp https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#argon2-encoding var argon2HashRegexp = regexp.MustCompile("^[$](?Pargon2(d|i|id))[$]v=(?P(16|19))[$]m=(?P[0-9]+),t=(?P[0-9]+),p=(?P

[0-9]+)(,keyid=(?P[^,]+))?(,data=(?P[^$]+))?[$](?P[^$]+)[$](?P.+)$") -func compareHashAndPasswordArgon2(ctx context.Context, hash, password string) error { +type Argon2HashInput struct { + alg string + v string + memory uint64 + time uint64 + threads uint64 + keyid string + data string + salt []byte + rawHash []byte +} + +func ParseArgon2Hash(hash string) (*Argon2HashInput, error) { submatch := argon2HashRegexp.FindStringSubmatchIndex(hash) if submatch == nil { - return errors.New("crypto: incorrect argon2 hash format") + return nil, errors.New("crypto: incorrect argon2 hash format") } alg := string(argon2HashRegexp.ExpandString(nil, "$alg", hash, submatch)) @@ -72,58 +86,68 @@ func compareHashAndPasswordArgon2(ctx context.Context, hash, password string) er hashB64 := string(argon2HashRegexp.ExpandString(nil, "$hash", hash, submatch)) if alg != "argon2i" && alg != "argon2id" { - return fmt.Errorf("crypto: argon2 hash uses unsupported algorithm %q only argon2i and argon2id supported", alg) + return nil, fmt.Errorf("crypto: argon2 hash uses unsupported algorithm %q only argon2i and argon2id supported", alg) } if v != "19" { - return fmt.Errorf("crypto: argon2 hash uses unsupported version %q only %d is supported", v, argon2.Version) + return nil, fmt.Errorf("crypto: argon2 hash uses unsupported version %q only %d is supported", v, argon2.Version) } if data != "" { - return fmt.Errorf("crypto: argon2 hashes with the data parameter not supported") + return nil, fmt.Errorf("crypto: argon2 hashes with the data parameter not supported") } if keyid != "" { - return fmt.Errorf("crypto: argon2 hashes with the keyid parameter not supported") + return nil, fmt.Errorf("crypto: argon2 hashes with the keyid parameter not supported") } memory, err := strconv.ParseUint(m, 10, 32) if err != nil { - return fmt.Errorf("crypto: argon2 hash has invalid m parameter %q %w", m, err) + return nil, fmt.Errorf("crypto: argon2 hash has invalid m parameter %q %w", m, err) } time, err := strconv.ParseUint(t, 10, 32) if err != nil { - return fmt.Errorf("crypto: argon2 hash has invalid t parameter %q %w", t, err) + return nil, fmt.Errorf("crypto: argon2 hash has invalid t parameter %q %w", t, err) } threads, err := strconv.ParseUint(p, 10, 8) if err != nil { - return fmt.Errorf("crypto: argon2 hash has invalid p parameter %q %w", p, err) + return nil, fmt.Errorf("crypto: argon2 hash has invalid p parameter %q %w", p, err) } rawHash, err := base64.RawStdEncoding.DecodeString(hashB64) if err != nil { - return fmt.Errorf("crypto: argon2 hash has invalid base64 in the hash section %w", err) + return nil, fmt.Errorf("crypto: argon2 hash has invalid base64 in the hash section %w", err) } salt, err := base64.RawStdEncoding.DecodeString(saltB64) if err != nil { - return fmt.Errorf("crypto: argon2 hash has invalid base64 in the salt section %w", err) + return nil, fmt.Errorf("crypto: argon2 hash has invalid base64 in the salt section %w", err) } - var match bool - var derivedKey []byte + input := Argon2HashInput{alg, v, memory, time, threads, keyid, data, salt, rawHash} + + return &input, nil +} + +func compareHashAndPasswordArgon2(ctx context.Context, hash, password string) error { + input, err := ParseArgon2Hash(hash) + if err != nil { + return err + } attributes := []attribute.KeyValue{ - attribute.String("alg", alg), - attribute.String("v", v), - attribute.Int64("m", int64(memory)), - attribute.Int64("t", int64(time)), - attribute.Int("p", int(threads)), - attribute.Int("len", len(rawHash)), + attribute.String("alg", input.alg), + attribute.String("v", input.v), + attribute.Int64("m", int64(input.memory)), + attribute.Int64("t", int64(input.time)), + attribute.Int("p", int(input.threads)), + attribute.Int("len", len(input.rawHash)), } + var match bool + var derivedKey []byte compareHashAndPasswordSubmittedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) defer func() { attributes = append(attributes, attribute.Bool( @@ -134,15 +158,15 @@ func compareHashAndPasswordArgon2(ctx context.Context, hash, password string) er compareHashAndPasswordCompletedCounter.Add(ctx, 1, metric.WithAttributes(attributes...)) }() - switch alg { + switch input.alg { case "argon2i": - derivedKey = argon2.Key([]byte(password), salt, uint32(time), uint32(memory)*1024, uint8(threads), uint32(len(rawHash))) + derivedKey = argon2.Key([]byte(password), input.salt, uint32(input.time), uint32(input.memory)*1024, uint8(input.threads), uint32(len(input.rawHash))) case "argon2id": - derivedKey = argon2.IDKey([]byte(password), salt, uint32(time), uint32(memory)*1024, uint8(threads), uint32(len(rawHash))) + derivedKey = argon2.IDKey([]byte(password), input.salt, uint32(input.time), uint32(input.memory)*1024, uint8(input.threads), uint32(len(input.rawHash))) } - match = subtle.ConstantTimeCompare(derivedKey, rawHash) == 0 + match = subtle.ConstantTimeCompare(derivedKey, input.rawHash) == 0 if !match { return ErrArgon2MismatchedHashAndPassword @@ -155,7 +179,7 @@ func compareHashAndPasswordArgon2(ctx context.Context, hash, password string) er // password, returns nil if equal otherwise an error. Context can be used to // cancel the hashing if the algorithm supports it. func CompareHashAndPassword(ctx context.Context, hash, password string) error { - if strings.HasPrefix(hash, "$argon2") { + if strings.HasPrefix(hash, Argon2Prefix) { return compareHashAndPasswordArgon2(ctx, hash, password) } @@ -181,7 +205,6 @@ func CompareHashAndPassword(ctx context.Context, hash, password string) error { }() err = bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err } diff --git a/internal/models/user.go b/internal/models/user.go index 0d074562a..3c871e543 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -14,6 +14,7 @@ import ( "github.com/pkg/errors" "github.com/supabase/auth/internal/crypto" "github.com/supabase/auth/internal/storage" + "golang.org/x/crypto/bcrypt" ) // User respresents a registered user with email/password authentication @@ -71,6 +72,31 @@ type User struct { DONTUSEINSTANCEID uuid.UUID `json:"-" db:"instance_id"` } +func NewUserWithPasswordHash(phone, email, passwordHash, aud string, userData map[string]interface{}) (*User, error) { + if strings.HasPrefix(passwordHash, crypto.Argon2Prefix) { + _, err := crypto.ParseArgon2Hash(passwordHash) + if err != nil { + return nil, err + } + } else { + // verify that the hash is a bcrypt hash + _, err := bcrypt.Cost([]byte(passwordHash)) + if err != nil { + return nil, err + } + } + id := uuid.Must(uuid.NewV4()) + user := &User{ + ID: id, + Aud: aud, + Email: storage.NullString(strings.ToLower(email)), + Phone: storage.NullString(phone), + UserMetaData: userData, + EncryptedPassword: &passwordHash, + } + return user, nil +} + // NewUser initializes a new user from an email, password and user data. func NewUser(phone, email, password, aud string, userData map[string]interface{}) (*User, error) { passwordHash := "" @@ -351,7 +377,7 @@ func (u *User) UpdatePassword(tx *storage.Connection, sessionID *uuid.UUID) erro } // Authenticate a user from a password -func (u *User) Authenticate(ctx context.Context, password string, decryptionKeys map[string]string, encrypt bool, encryptionKeyID string) (bool, bool, error) { +func (u *User) Authenticate(ctx context.Context, tx *storage.Connection, password string, decryptionKeys map[string]string, encrypt bool, encryptionKeyID string) (bool, bool, error) { if u.EncryptedPassword == nil { return false, false, nil } @@ -370,6 +396,22 @@ func (u *User) Authenticate(ctx context.Context, password string, decryptionKeys compareErr := crypto.CompareHashAndPassword(ctx, hash, password) + if !strings.HasPrefix(hash, crypto.Argon2Prefix) { + // check if cost exceeds default cost or is too low + cost, err := bcrypt.Cost([]byte(hash)) + if err != nil { + return compareErr == nil, false, err + } + + if cost > bcrypt.DefaultCost || cost == bcrypt.MinCost { + // don't bother with encrypting the password in Authenticate + // since it's handled separately + if err := u.SetPassword(ctx, password, false, "", ""); err != nil { + return compareErr == nil, false, err + } + } + } + return compareErr == nil, encrypt && (es == nil || es.ShouldReEncrypt(encryptionKeyID)), nil } diff --git a/internal/models/user_test.go b/internal/models/user_test.go index 011cf28f0..47d16178d 100644 --- a/internal/models/user_test.go +++ b/internal/models/user_test.go @@ -1,6 +1,7 @@ package models import ( + "context" "strings" "testing" @@ -11,6 +12,7 @@ import ( "github.com/supabase/auth/internal/crypto" "github.com/supabase/auth/internal/storage" "github.com/supabase/auth/internal/storage/test" + "golang.org/x/crypto/bcrypt" ) const modelsTestConfig = "../../hack/test.env" @@ -378,3 +380,93 @@ func (ts *UserTestSuite) TestSetPasswordTooLong() { err = user.SetPassword(ts.db.Context(), strings.Repeat("a", crypto.MaxPasswordLength), false, "", "") require.NoError(ts.T(), err) } + +func (ts *UserTestSuite) TestNewUserWithPasswordHashSuccess() { + cases := []struct { + desc string + hash string + }{ + { + desc: "Valid bcrypt hash", + hash: "$2y$10$SXEz2HeT8PUIGQXo9yeUIem8KzNxgG0d7o/.eGj2rj8KbRgAuRVlq", + }, + { + desc: "Valid argon2i hash", + hash: "$argon2i$v=19$m=16,t=2,p=1$bGJRWThNOHJJTVBSdHl2dQ$NfEnUOuUpb7F2fQkgFUG4g", + }, + { + desc: "Valid argon2id hash", + hash: "$argon2id$v=19$m=32,t=3,p=2$SFVpOWJ0eXhjRzVkdGN1RQ$RXnb8rh7LaDcn07xsssqqulZYXOM/EUCEFMVcAcyYVk", + }, + } + + for _, c := range cases { + ts.Run(c.desc, func() { + u, err := NewUserWithPasswordHash("", "", c.hash, "", nil) + require.NoError(ts.T(), err) + require.NotNil(ts.T(), u) + }) + } +} + +func (ts *UserTestSuite) TestNewUserWithPasswordHashFailure() { + cases := []struct { + desc string + hash string + }{ + { + desc: "Invalid argon2i hash", + hash: "$argon2id$test", + }, + { + desc: "Invalid bcrypt hash", + hash: "plaintest_password", + }, + } + + for _, c := range cases { + ts.Run(c.desc, func() { + u, err := NewUserWithPasswordHash("", "", c.hash, "", nil) + require.Error(ts.T(), err) + require.Nil(ts.T(), u) + }) + } +} + +func (ts *UserTestSuite) TestAuthenticate() { + // every case uses "test" as the password + cases := []struct { + desc string + hash string + expectedHashCost int + }{ + { + desc: "Invalid bcrypt hash cost of 11", + hash: "$2y$11$4lH57PU7bGATpRcx93vIoObH3qDmft/pytbOzDG9/1WsyNmN5u4di", + expectedHashCost: bcrypt.MinCost, + }, + { + desc: "Valid bcrypt hash cost of 10", + hash: "$2y$10$va66S4MxFrH6G6L7BzYl0.QgcYgvSr/F92gc.3botlz7bG4p/g/1i", + expectedHashCost: bcrypt.DefaultCost, + }, + } + + for _, c := range cases { + ts.Run(c.desc, func() { + u, err := NewUserWithPasswordHash("", "", c.hash, "", nil) + require.NoError(ts.T(), err) + require.NoError(ts.T(), ts.db.Create(u)) + require.NotNil(ts.T(), u) + + isAuthenticated, _, err := u.Authenticate(context.Background(), ts.db, "test", nil, false, "") + require.NoError(ts.T(), err) + require.True(ts.T(), isAuthenticated) + + // check hash cost + hashCost, err := bcrypt.Cost([]byte(*u.EncryptedPassword)) + require.NoError(ts.T(), err) + require.Equal(ts.T(), c.expectedHashCost, hashCost) + }) + } +} From 85361b7aa533381ae14acf92d5c0efbe2e218997 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:10:34 +0200 Subject: [PATCH 24/42] chore(master): release 2.155.0 (#1640) :robot: I have created a release *beep* *boop* --- ## [2.155.0](https://github.com/supabase/auth/compare/v2.154.2...v2.155.0) (2024-07-03) ### Features * add `password_hash` and `id` fields to admin create user ([#1641](https://github.com/supabase/auth/issues/1641)) ([20d59f1](https://github.com/supabase/auth/commit/20d59f10b601577683d05bcd7d2128ff4bc462a0)) ### Bug Fixes * improve mfa verify logs ([#1635](https://github.com/supabase/auth/issues/1635)) ([d8b47f9](https://github.com/supabase/auth/commit/d8b47f9d3f0dc8f97ad1de49e45f452ebc726481)) * invited users should have a temporary password generated ([#1644](https://github.com/supabase/auth/issues/1644)) ([3f70d9d](https://github.com/supabase/auth/commit/3f70d9d8974d0e9c437c51e1312ad17ce9056ec9)) * upgrade golang-jwt to v5 ([#1639](https://github.com/supabase/auth/issues/1639)) ([2cb97f0](https://github.com/supabase/auth/commit/2cb97f080fa4695766985cc4792d09476534be68)) * use pointer for `user.EncryptedPassword` ([#1637](https://github.com/supabase/auth/issues/1637)) ([bbecbd6](https://github.com/supabase/auth/commit/bbecbd61a46b0c528b1191f48d51f166c06f4b16)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c708a5779..62fc5a48c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [2.155.0](https://github.com/supabase/auth/compare/v2.154.2...v2.155.0) (2024-07-03) + + +### Features + +* add `password_hash` and `id` fields to admin create user ([#1641](https://github.com/supabase/auth/issues/1641)) ([20d59f1](https://github.com/supabase/auth/commit/20d59f10b601577683d05bcd7d2128ff4bc462a0)) + + +### Bug Fixes + +* improve mfa verify logs ([#1635](https://github.com/supabase/auth/issues/1635)) ([d8b47f9](https://github.com/supabase/auth/commit/d8b47f9d3f0dc8f97ad1de49e45f452ebc726481)) +* invited users should have a temporary password generated ([#1644](https://github.com/supabase/auth/issues/1644)) ([3f70d9d](https://github.com/supabase/auth/commit/3f70d9d8974d0e9c437c51e1312ad17ce9056ec9)) +* upgrade golang-jwt to v5 ([#1639](https://github.com/supabase/auth/issues/1639)) ([2cb97f0](https://github.com/supabase/auth/commit/2cb97f080fa4695766985cc4792d09476534be68)) +* use pointer for `user.EncryptedPassword` ([#1637](https://github.com/supabase/auth/issues/1637)) ([bbecbd6](https://github.com/supabase/auth/commit/bbecbd61a46b0c528b1191f48d51f166c06f4b16)) + ## [2.154.2](https://github.com/supabase/auth/compare/v2.154.1...v2.154.2) (2024-06-24) From 3c8d7656431ac4b2e80726b7c37adb8f0c778495 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 4 Jul 2024 00:28:13 -0700 Subject: [PATCH 25/42] fix: return proper error if sms rate limit is exceeded (#1647) ## What kind of change does this PR introduce? * Fixes #1629 * Previously, rate limit errors were being returned as an internal server error, which is incorrect. This PR checks for the underlying error type returned and ensures that rate limit errors are surfaced appropriately. ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- internal/api/user.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/api/user.go b/internal/api/user.go index 4cb1fc1d9..f456db3fd 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -236,6 +236,9 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { return internalServerError("Error finding SMS provider").WithInternalError(terr) } if _, terr := a.sendPhoneConfirmation(r, tx, user, params.Phone, phoneChangeVerification, smsProvider, params.Channel); terr != nil { + if errors.Is(terr, MaxFrequencyLimitError) { + return tooManyRequestsError(ErrorCodeOverSMSSendRateLimit, generateFrequencyLimitErrorMessage(user.PhoneChangeSentAt, config.Sms.MaxFrequency)) + } return internalServerError("Error sending phone change otp").WithInternalError(terr) } } From 42c1d4526b98203664d4a22c23014ecd0b4951f9 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 4 Jul 2024 11:44:32 -0700 Subject: [PATCH 26/42] fix: check for empty aud string (#1649) ## What kind of change does this PR introduce? * Fixes an issue where an empty string set as the `aud` claim will cause some methods to fail ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- internal/api/helpers.go | 2 +- internal/api/helpers_test.go | 77 ++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/internal/api/helpers.go b/internal/api/helpers.go index 58ac82d08..485a3870c 100644 --- a/internal/api/helpers.go +++ b/internal/api/helpers.go @@ -39,7 +39,7 @@ func (a *API) requestAud(ctx context.Context, r *http.Request) string { if claims != nil { aud, _ := claims.GetAudience() - if len(aud) != 0 { + if len(aud) != 0 && aud[0] != "" { return aud[0] } } diff --git a/internal/api/helpers_test.go b/internal/api/helpers_test.go index 84a4846e6..29070e81d 100644 --- a/internal/api/helpers_test.go +++ b/internal/api/helpers_test.go @@ -1,10 +1,15 @@ package api import ( + "fmt" + "net/http" + "net/http/httptest" "strconv" "testing" + "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" + "github.com/supabase/auth/internal/conf" ) func TestIsValidCodeChallenge(t *testing.T) { @@ -72,3 +77,75 @@ func TestIsValidPKCEParams(t *testing.T) { }) } } + +func TestRequestAud(ts *testing.T) { + mockAPI := API{ + config: &conf.GlobalConfiguration{ + JWT: conf.JWTConfiguration{ + Aud: "authenticated", + Secret: "test-secret", + }, + }, + } + + cases := []struct { + desc string + headers map[string]string + payload map[string]interface{} + expectedAud string + }{ + { + desc: "Valid audience slice", + headers: map[string]string{ + audHeaderName: "my_custom_aud", + }, + payload: map[string]interface{}{ + "aud": "authenticated", + }, + expectedAud: "my_custom_aud", + }, + { + desc: "Valid custom audience", + payload: map[string]interface{}{ + "aud": "my_custom_aud", + }, + expectedAud: "my_custom_aud", + }, + { + desc: "Invalid audience", + payload: map[string]interface{}{ + "aud": "", + }, + expectedAud: mockAPI.config.JWT.Aud, + }, + { + desc: "Missing audience", + payload: map[string]interface{}{ + "sub": "d6044b6e-b0ec-4efe-a055-0d2d6ff1dbd8", + }, + expectedAud: mockAPI.config.JWT.Aud, + }, + } + + for _, c := range cases { + ts.Run(c.desc, func(t *testing.T) { + claims := jwt.MapClaims(c.payload) + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + signed, err := token.SignedString([]byte(mockAPI.config.JWT.Secret)) + require.NoError(t, err) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("Authorization", fmt.Sprintf("Bearer: %s", signed)) + for k, v := range c.headers { + req.Header.Set(k, v) + } + + // set the token in the request context for requestAud + ctx, err := mockAPI.parseJWTClaims(signed, req) + require.NoError(t, err) + aud := mockAPI.requestAud(ctx, req) + require.Equal(t, c.expectedAud, aud) + }) + } + +} From a5185058e72509b0781e0eb59910ecdbb8676fee Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 4 Jul 2024 14:47:09 -0700 Subject: [PATCH 27/42] fix: apply mailer autoconfirm config to update user email (#1646) ## What kind of change does this PR introduce? * `GOTRUE_MAILER_AUTOCONFIRM` setting should be respected in the update email flow via `PUT /user` ## What is the current behavior? * When `GOTRUE_MAILER_AUTOCONFIRM=true`, updating a user's email still sends an email and requires user confirmation * Difficult for anonymous users to upgrade to a permanent user seamlessly without requiring email confirmation * Fixes #1619 ## What is the new behavior? * When `GOTRUE_MAILER_AUTOCONFIRM=true`, updating a user's email will not require email confirmation. ## Additional context Add any other context or screenshots. --- internal/api/user.go | 30 +++++++++++++++++++++--------- internal/api/user_test.go | 28 ++++++++++++++++++++++++++++ internal/api/verify.go | 5 ++++- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/internal/api/user.go b/internal/api/user.go index f456db3fd..6fec273f9 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -8,6 +8,7 @@ import ( "github.com/gofrs/uuid" "github.com/supabase/auth/internal/api/sms_provider" + "github.com/supabase/auth/internal/mailer" "github.com/supabase/auth/internal/models" "github.com/supabase/auth/internal/storage" ) @@ -205,19 +206,30 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { } if params.Email != "" && params.Email != user.GetEmail() { - flowType := getFlowFromChallenge(params.CodeChallenge) - if isPKCEFlow(flowType) { - _, terr := generateFlowState(tx, models.EmailChange.String(), models.EmailChange, params.CodeChallengeMethod, params.CodeChallenge, &user.ID) - if terr != nil { + if config.Mailer.Autoconfirm { + user.EmailChange = params.Email + if _, terr := a.emailChangeVerify(r, tx, &VerifyParams{ + Type: mailer.EmailChangeVerification, + Email: params.Email, + }, user); terr != nil { return terr } - } - if terr = a.sendEmailChange(r, tx, user, params.Email, flowType); terr != nil { - if errors.Is(terr, MaxFrequencyLimitError) { - return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, generateFrequencyLimitErrorMessage(user.EmailChangeSentAt, config.SMTP.MaxFrequency)) + } else { + flowType := getFlowFromChallenge(params.CodeChallenge) + if isPKCEFlow(flowType) { + _, terr := generateFlowState(tx, models.EmailChange.String(), models.EmailChange, params.CodeChallengeMethod, params.CodeChallenge, &user.ID) + if terr != nil { + return terr + } + + } + if terr = a.sendEmailChange(r, tx, user, params.Email, flowType); terr != nil { + if errors.Is(terr, MaxFrequencyLimitError) { + return tooManyRequestsError(ErrorCodeOverEmailSendRateLimit, generateFrequencyLimitErrorMessage(user.EmailChangeSentAt, config.SMTP.MaxFrequency)) + } + return internalServerError("Error sending change email").WithInternalError(terr) } - return internalServerError("Error sending change email").WithInternalError(terr) } } diff --git a/internal/api/user_test.go b/internal/api/user_test.go index af9cfec37..cef28d607 100644 --- a/internal/api/user_test.go +++ b/internal/api/user_test.go @@ -84,6 +84,7 @@ func (ts *UserTestSuite) TestUserUpdateEmail() { desc string userData map[string]string isSecureEmailChangeEnabled bool + isMailerAutoconfirmEnabled bool expectedCode int }{ { @@ -93,6 +94,7 @@ func (ts *UserTestSuite) TestUserUpdateEmail() { "phone": "", }, isSecureEmailChangeEnabled: false, + isMailerAutoconfirmEnabled: false, expectedCode: http.StatusOK, }, { @@ -102,6 +104,7 @@ func (ts *UserTestSuite) TestUserUpdateEmail() { "phone": "234567890", }, isSecureEmailChangeEnabled: true, + isMailerAutoconfirmEnabled: false, expectedCode: http.StatusOK, }, { @@ -111,6 +114,7 @@ func (ts *UserTestSuite) TestUserUpdateEmail() { "phone": "", }, isSecureEmailChangeEnabled: false, + isMailerAutoconfirmEnabled: false, expectedCode: http.StatusOK, }, { @@ -120,6 +124,17 @@ func (ts *UserTestSuite) TestUserUpdateEmail() { "phone": "", }, isSecureEmailChangeEnabled: true, + isMailerAutoconfirmEnabled: false, + expectedCode: http.StatusOK, + }, + { + desc: "Update email with mailer autoconfirm enabled", + userData: map[string]string{ + "email": "bar@example.com", + "phone": "", + }, + isSecureEmailChangeEnabled: true, + isMailerAutoconfirmEnabled: true, expectedCode: http.StatusOK, }, } @@ -146,9 +161,22 @@ func (ts *UserTestSuite) TestUserUpdateEmail() { w := httptest.NewRecorder() ts.Config.Mailer.SecureEmailChangeEnabled = c.isSecureEmailChangeEnabled + ts.Config.Mailer.Autoconfirm = c.isMailerAutoconfirmEnabled ts.API.handler.ServeHTTP(w, req) require.Equal(ts.T(), c.expectedCode, w.Code) + var data models.User + require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data)) + + if c.isMailerAutoconfirmEnabled { + require.Empty(ts.T(), data.EmailChange) + require.Equal(ts.T(), "new@example.com", data.GetEmail()) + require.Len(ts.T(), data.Identities, 1) + } else { + require.Equal(ts.T(), "new@example.com", data.EmailChange) + require.Len(ts.T(), data.Identities, 0) + } + // remove user after each case require.NoError(ts.T(), ts.API.db.Destroy(u)) }) diff --git a/internal/api/verify.go b/internal/api/verify.go index c0b78d680..97a13b93b 100644 --- a/internal/api/verify.go +++ b/internal/api/verify.go @@ -491,7 +491,10 @@ func (a *API) prepPKCERedirectURL(rurl, code string) (string, error) { func (a *API) emailChangeVerify(r *http.Request, conn *storage.Connection, params *VerifyParams, user *models.User) (*models.User, error) { config := a.config - if config.Mailer.SecureEmailChangeEnabled && user.EmailChangeConfirmStatus == zeroConfirmation && user.GetEmail() != "" { + if !config.Mailer.Autoconfirm && + config.Mailer.SecureEmailChangeEnabled && + user.EmailChangeConfirmStatus == zeroConfirmation && + user.GetEmail() != "" { err := conn.Transaction(func(tx *storage.Connection) error { currentOTT, terr := models.FindOneTimeToken(tx, params.TokenHash, models.EmailChangeTokenCurrent) if terr != nil && !models.IsNotFoundError(terr) { From 9c88ee55c61a9246f7f89df32c25e96a3000d3d2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 4 Jul 2024 14:48:34 -0700 Subject: [PATCH 28/42] chore(master): release 2.155.1 (#1648) :robot: I have created a release *beep* *boop* --- ## [2.155.1](https://github.com/supabase/auth/compare/v2.155.0...v2.155.1) (2024-07-04) ### Bug Fixes * apply mailer autoconfirm config to update user email ([#1646](https://github.com/supabase/auth/issues/1646)) ([a518505](https://github.com/supabase/auth/commit/a5185058e72509b0781e0eb59910ecdbb8676fee)) * check for empty aud string ([#1649](https://github.com/supabase/auth/issues/1649)) ([42c1d45](https://github.com/supabase/auth/commit/42c1d4526b98203664d4a22c23014ecd0b4951f9)) * return proper error if sms rate limit is exceeded ([#1647](https://github.com/supabase/auth/issues/1647)) ([3c8d765](https://github.com/supabase/auth/commit/3c8d7656431ac4b2e80726b7c37adb8f0c778495)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62fc5a48c..a82a5f385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [2.155.1](https://github.com/supabase/auth/compare/v2.155.0...v2.155.1) (2024-07-04) + + +### Bug Fixes + +* apply mailer autoconfirm config to update user email ([#1646](https://github.com/supabase/auth/issues/1646)) ([a518505](https://github.com/supabase/auth/commit/a5185058e72509b0781e0eb59910ecdbb8676fee)) +* check for empty aud string ([#1649](https://github.com/supabase/auth/issues/1649)) ([42c1d45](https://github.com/supabase/auth/commit/42c1d4526b98203664d4a22c23014ecd0b4951f9)) +* return proper error if sms rate limit is exceeded ([#1647](https://github.com/supabase/auth/issues/1647)) ([3c8d765](https://github.com/supabase/auth/commit/3c8d7656431ac4b2e80726b7c37adb8f0c778495)) + ## [2.155.0](https://github.com/supabase/auth/compare/v2.154.2...v2.155.0) (2024-07-03) From 33caaa9893f9343ac175d1c38742ba2e91f4dfc0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 15:24:18 -0700 Subject: [PATCH 29/42] chore(deps): bump github.com/rs/cors from 1.9.0 to 1.11.0 (#1650) Bumps [github.com/rs/cors](https://github.com/rs/cors) from 1.9.0 to 1.11.0.

Commits
  • 4c32059 Normalize allowed request headers and store them in a sorted set (fixes #170)...
  • 8d33ca4 Complete documentation; deprecate AllowOriginRequestFunc in favour of AllowOr...
  • af821ae Merge branch 'jub0bs-master'
  • 0bcf73f Update benchmark
  • eacc8e8 Fix skewed middleware benchmarks (#165)
  • 9297f15 Respect the documented precedence of options (#163)
  • 73f81b4 Fix readme benchmark rendering (#161)
  • e19471c Prevent empty Access-Control-Expose-Headers header (#160)
  • 20a76bd Update benchmark
  • 46855ae Remove travis build report from README
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/rs/cors&package-manager=go_modules&previous-version=1.9.0&new-version=1.11.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/supabase/auth/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 15c9a6651..cdea6cfac 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 - github.com/rs/cors v1.9.0 + github.com/rs/cors v1.11.0 github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35 github.com/sethvargo/go-password v0.2.0 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index 66067a406..1cf79bda8 100644 --- a/go.sum +++ b/go.sum @@ -252,8 +252,6 @@ 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= -github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= @@ -270,8 +268,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= -github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= From 10ca9c806e4b67a371897f1b3f93c515764c4240 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 11 Jul 2024 02:17:38 -0700 Subject: [PATCH 30/42] fix: set rate limit log level to warn (#1652) --- internal/api/errors.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/api/errors.go b/internal/api/errors.go index e821409a7..308a85a0f 100644 --- a/internal/api/errors.go +++ b/internal/api/errors.go @@ -232,11 +232,14 @@ func HandleResponseError(err error, w http.ResponseWriter, r *http.Request) { } case *HTTPError: - if e.HTTPStatus >= http.StatusInternalServerError { + switch { + case e.HTTPStatus >= http.StatusInternalServerError: e.ErrorID = errorID // this will get us the stack trace too log.WithError(e.Cause()).Error(e.Error()) - } else { + case e.HTTPStatus == http.StatusTooManyRequests: + log.WithError(e.Cause()).Warn(e.Error()) + default: log.WithError(e.Cause()).Info(e.Error()) } From 4e6ef47184ce7bb7acb5e8c155d2537ec9b9fad2 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 11 Jul 2024 15:39:29 -0700 Subject: [PATCH 31/42] chore: add default tests for saml assertions (#1651) ## What kind of change does this PR introduce? * Add test cases to ensure that the default attribute is respected * If the attribute is missing, the default value in the mapping will be used * If the attribute is present, but the mapping doesn't specify the name, then the default value will be used * The name will always be respected over the default ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --------- Co-authored-by: Joel Lee --- internal/api/samlassertion_test.go | 92 +++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/internal/api/samlassertion_test.go b/internal/api/samlassertion_test.go index 47992a214..62c3e52f4 100644 --- a/internal/api/samlassertion_test.go +++ b/internal/api/samlassertion_test.go @@ -150,6 +150,7 @@ func TestSAMLAssertionUserID(t *tst.T) { func TestSAMLAssertionProcessing(t *tst.T) { type spec struct { + desc string xml string mapping models.SAMLAttributeMapping expected map[string]interface{} @@ -157,6 +158,7 @@ func TestSAMLAssertionProcessing(t *tst.T) { examples := []spec{ { + desc: "valid attribute and mapping", xml: ` @@ -178,6 +180,7 @@ func TestSAMLAssertionProcessing(t *tst.T) { }, }, { + desc: "valid attributes, use first attribute found in Names", xml: ` @@ -205,6 +208,7 @@ func TestSAMLAssertionProcessing(t *tst.T) { }, }, { + desc: "valid groups attribute", xml: ` @@ -240,6 +244,92 @@ func TestSAMLAssertionProcessing(t *tst.T) { }, }, }, + { + desc: "missing attribute, use default value", + xml: ` + + + + someone@example.com + + + +`, + mapping: models.SAMLAttributeMapping{ + Keys: map[string]models.SAMLAttribute{ + "email": { + Name: "mail", + }, + "role": { + Name: "role", + Default: "member", + }, + }, + }, + expected: map[string]interface{}{ + "email": "someone@example.com", + "role": "member", + }, + }, + { + desc: "use default value even if attribute exists but is not specified in mapping", + xml: ` + + + + someone@example.com + + + admin + + + +`, + mapping: models.SAMLAttributeMapping{ + Keys: map[string]models.SAMLAttribute{ + "email": { + Name: "mail", + }, + "role": { + Default: "member", + }, + }, + }, + expected: map[string]interface{}{ + "email": "someone@example.com", + "role": "member", + }, + }, + { + desc: "use value in XML when attribute exists and is specified in mapping", + xml: ` + + + + someone@example.com + + + admin + + + +`, + mapping: models.SAMLAttributeMapping{ + Keys: map[string]models.SAMLAttribute{ + "email": { + Name: "mail", + }, + "role": { + Name: "role", + Default: "member", + }, + }, + }, + expected: map[string]interface{}{ + "email": "someone@example.com", + "role": "admin", + }, + }, } for i, example := range examples { @@ -252,6 +342,6 @@ func TestSAMLAssertionProcessing(t *tst.T) { result := assertion.Process(example.mapping) - require.Equal(t, result, example.expected, "example %d had different processing", i) + require.Equal(t, example.expected, result, "example %d had different processing", i) } } From bf5381a6b1c686955dc4e39fe5fb806ffd309563 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Fri, 12 Jul 2024 02:43:06 -0700 Subject: [PATCH 32/42] fix: omit empty string from name & use case-insensitive equality for comparing SAML attributes (#1654) ## What kind of change does this PR introduce? * Fixes an issue where the SAML mapping is incorrect when a `Default` is specified in the mapping and the `FriendlyName` in the SAML Assertion is an empty string * Use case-insensitive equality for mapping SAML attributes * Tested with a trial okta account ## What is the current behavior? * During the SAML assertion process, the `SAMLAttributeMapping` struct defaults the `Name` to an empty string. When combined with a SAML assertion that has an empty string set in the `FriendlyName`, this causes an incorrect mapping when a default mapping is provided. (see [test case](https://github.com/supabase/auth/pull/1654/files#diff-e708a1335880a92063e8b45f3a06eb96931ab0ee46b94cdf6bf5f566640f7a3cR247-R272) * Use case-sensitive equality for mapping SAML attributes Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- internal/api/samlassertion.go | 8 +- internal/api/samlassertion_test.go | 130 ++++++++++++++--------------- 2 files changed, 70 insertions(+), 68 deletions(-) diff --git a/internal/api/samlassertion.go b/internal/api/samlassertion.go index 75cdfdb4f..fdf932385 100644 --- a/internal/api/samlassertion.go +++ b/internal/api/samlassertion.go @@ -24,8 +24,7 @@ func (a *SAMLAssertion) Attribute(name string) []saml.AttributeValue { for _, stmt := range a.AttributeStatements { for _, attr := range stmt.Attributes { - // TODO: maybe this should be case-insentivite equality? - if attr.Name == name || attr.FriendlyName == name { + if strings.EqualFold(attr.Name, name) || strings.EqualFold(attr.FriendlyName, name) { values = append(values, attr.Values...) } } @@ -120,7 +119,10 @@ func (a *SAMLAssertion) Process(mapping models.SAMLAttributeMapping) map[string] ret := make(map[string]interface{}) for key, mapper := range mapping.Keys { - names := []string{mapper.Name} + names := []string{} + if mapper.Name != "" { + names = append(names, mapper.Name) + } names = append(names, mapper.Names...) setKey := false diff --git a/internal/api/samlassertion_test.go b/internal/api/samlassertion_test.go index 62c3e52f4..b7461b26d 100644 --- a/internal/api/samlassertion_test.go +++ b/internal/api/samlassertion_test.go @@ -160,14 +160,14 @@ func TestSAMLAssertionProcessing(t *tst.T) { { desc: "valid attribute and mapping", xml: ` - - - - someone@example.com - - - -`, + + + + someone@example.com + + + + `, mapping: models.SAMLAttributeMapping{ Keys: map[string]models.SAMLAttribute{ "email": { @@ -182,17 +182,17 @@ func TestSAMLAssertionProcessing(t *tst.T) { { desc: "valid attributes, use first attribute found in Names", xml: ` - - - - old-soap@example.com - - - soap@example.com - - - -`, + + + + old-soap@example.com + + + soap@example.com + + + + `, mapping: models.SAMLAttributeMapping{ Keys: map[string]models.SAMLAttribute{ "email": { @@ -210,18 +210,18 @@ func TestSAMLAssertionProcessing(t *tst.T) { { desc: "valid groups attribute", xml: ` - - - - group1 - group2 - - - soap@example.com - - - -`, + + + + group1 + group2 + + + soap@example.com + + + + `, mapping: models.SAMLAttributeMapping{ Keys: map[string]models.SAMLAttribute{ "email": { @@ -245,11 +245,11 @@ func TestSAMLAssertionProcessing(t *tst.T) { }, }, { - desc: "missing attribute, use default value", + desc: "missing attribute use default value", xml: ` - + someone@example.com @@ -258,10 +258,9 @@ func TestSAMLAssertionProcessing(t *tst.T) { mapping: models.SAMLAttributeMapping{ Keys: map[string]models.SAMLAttribute{ "email": { - Name: "mail", + Name: "email", }, "role": { - Name: "role", Default: "member", }, }, @@ -274,17 +273,17 @@ func TestSAMLAssertionProcessing(t *tst.T) { { desc: "use default value even if attribute exists but is not specified in mapping", xml: ` - - - - someone@example.com - - - admin - - - -`, + + + + someone@example.com + + + admin + + + + `, mapping: models.SAMLAttributeMapping{ Keys: map[string]models.SAMLAttribute{ "email": { @@ -303,17 +302,17 @@ func TestSAMLAssertionProcessing(t *tst.T) { { desc: "use value in XML when attribute exists and is specified in mapping", xml: ` - - - - someone@example.com - - - admin - - - -`, + + + + someone@example.com + + + admin + + + + `, mapping: models.SAMLAttributeMapping{ Keys: map[string]models.SAMLAttribute{ "email": { @@ -333,15 +332,16 @@ func TestSAMLAssertionProcessing(t *tst.T) { } for i, example := range examples { - rawAssertion := saml.Assertion{} - require.NoError(t, xml.Unmarshal([]byte(example.xml), &rawAssertion)) - - assertion := SAMLAssertion{ - &rawAssertion, - } + t.Run(example.desc, func(t *tst.T) { + rawAssertion := saml.Assertion{} + require.NoError(t, xml.Unmarshal([]byte(example.xml), &rawAssertion)) - result := assertion.Process(example.mapping) + assertion := SAMLAssertion{ + &rawAssertion, + } - require.Equal(t, example.expected, result, "example %d had different processing", i) + result := assertion.Process(example.mapping) + require.Equal(t, example.expected, result, "example %d had different processing", i) + }) } } From 5a6793ee8fce7a089750fe10b3b63bb0a19d6d21 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Fri, 12 Jul 2024 02:43:20 -0700 Subject: [PATCH 33/42] fix: improve session error logging (#1655) ## What kind of change does this PR introduce? * Improve logging for missing session * Helps provide better visibility for missing session errors encountered in https://github.com/orgs/supabase/discussions/23715 ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- internal/api/auth.go | 2 +- internal/api/auth_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/api/auth.go b/internal/api/auth.go index 4e63715a1..d6d079364 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -123,7 +123,7 @@ func (a *API) maybeLoadUserOrSession(ctx context.Context) (context.Context, erro session, err = models.FindSessionByID(db, sessionId, false) if err != nil { if models.IsNotFoundError(err) { - return ctx, forbiddenError(ErrorCodeSessionNotFound, "Session from session_id claim in JWT does not exist") + return ctx, forbiddenError(ErrorCodeSessionNotFound, "Session from session_id claim in JWT does not exist").WithInternalError(err).WithInternalMessage(fmt.Sprintf("session id (%s) doesn't exist", sessionId)) } return ctx, err } diff --git a/internal/api/auth_test.go b/internal/api/auth_test.go index 35a64be92..3150628d9 100644 --- a/internal/api/auth_test.go +++ b/internal/api/auth_test.go @@ -167,7 +167,7 @@ func (ts *AuthTestSuite) TestMaybeLoadUserOrSession() { Role: "authenticated", SessionId: "73bf9ee0-9e8c-453b-b484-09cb93e2f341", }, - ExpectedError: forbiddenError(ErrorCodeSessionNotFound, "Session from session_id claim in JWT does not exist"), + ExpectedError: forbiddenError(ErrorCodeSessionNotFound, "Session from session_id claim in JWT does not exist").WithInternalError(models.SessionNotFoundError{}).WithInternalMessage("session id (73bf9ee0-9e8c-453b-b484-09cb93e2f341) doesn't exist"), ExpectedUser: u, ExpectedSession: nil, }, From ac619397892f7049351df76f86393a01f793e591 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 11:44:30 +0200 Subject: [PATCH 34/42] chore(master): release 2.155.2 (#1653) :robot: I have created a release *beep* *boop* --- ## [2.155.2](https://github.com/supabase/auth/compare/v2.155.1...v2.155.2) (2024-07-12) ### Bug Fixes * improve session error logging ([#1655](https://github.com/supabase/auth/issues/1655)) ([5a6793e](https://github.com/supabase/auth/commit/5a6793ee8fce7a089750fe10b3b63bb0a19d6d21)) * omit empty string from name & use case-insensitive equality for comparing SAML attributes ([#1654](https://github.com/supabase/auth/issues/1654)) ([bf5381a](https://github.com/supabase/auth/commit/bf5381a6b1c686955dc4e39fe5fb806ffd309563)) * set rate limit log level to warn ([#1652](https://github.com/supabase/auth/issues/1652)) ([10ca9c8](https://github.com/supabase/auth/commit/10ca9c806e4b67a371897f1b3f93c515764c4240)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a82a5f385..e9ad3d484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [2.155.2](https://github.com/supabase/auth/compare/v2.155.1...v2.155.2) (2024-07-12) + + +### Bug Fixes + +* improve session error logging ([#1655](https://github.com/supabase/auth/issues/1655)) ([5a6793e](https://github.com/supabase/auth/commit/5a6793ee8fce7a089750fe10b3b63bb0a19d6d21)) +* omit empty string from name & use case-insensitive equality for comparing SAML attributes ([#1654](https://github.com/supabase/auth/issues/1654)) ([bf5381a](https://github.com/supabase/auth/commit/bf5381a6b1c686955dc4e39fe5fb806ffd309563)) +* set rate limit log level to warn ([#1652](https://github.com/supabase/auth/issues/1652)) ([10ca9c8](https://github.com/supabase/auth/commit/10ca9c806e4b67a371897f1b3f93c515764c4240)) + ## [2.155.1](https://github.com/supabase/auth/compare/v2.155.0...v2.155.1) (2024-07-04) From 98d83245e40d606438eb0afdbf474276179fd91d Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Fri, 12 Jul 2024 09:20:55 -0700 Subject: [PATCH 35/42] fix: serialize jwt as string (#1657) ## What kind of change does this PR introduce? * serializes the jwt as a string rather than a slice of strings ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- internal/api/token.go | 4 +++- internal/hooks/auth_hooks.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/api/token.go b/internal/api/token.go index 22c42e9b1..3362faa93 100644 --- a/internal/api/token.go +++ b/internal/api/token.go @@ -329,7 +329,7 @@ func (a *API) generateAccessToken(r *http.Request, tx *storage.Connection, user claims := &hooks.AccessTokenClaims{ RegisteredClaims: jwt.RegisteredClaims{ Subject: user.ID.String(), - Audience: []string{user.Aud}, + Audience: jwt.ClaimStrings{user.Aud}, IssuedAt: jwt.NewNumericDate(issuedAt), ExpiresAt: jwt.NewNumericDate(expiresAt), Issuer: config.JWT.Issuer, @@ -375,6 +375,8 @@ func (a *API) generateAccessToken(r *http.Request, tx *storage.Connection, user token.Header["kid"] = config.JWT.KeyID } + // this serializes the aud claim was a string + jwt.MarshalSingleStringAsArray = false signed, err := token.SignedString([]byte(config.JWT.Secret)) if err != nil { return "", 0, err diff --git a/internal/hooks/auth_hooks.go b/internal/hooks/auth_hooks.go index c11b91b5e..315bff872 100644 --- a/internal/hooks/auth_hooks.go +++ b/internal/hooks/auth_hooks.go @@ -43,7 +43,7 @@ const MinimumViableTokenSchema = `{ "type": "object", "properties": { "aud": { - "type": "array" + "type": ["string", "array"] }, "exp": { "type": "integer" From 9ac3cffb9dd36958fd6f221765dcebcebf1a30d4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:25:42 -0700 Subject: [PATCH 36/42] chore(master): release 2.155.3 (#1658) :robot: I have created a release *beep* *boop* --- ## [2.155.3](https://github.com/supabase/auth/compare/v2.155.2...v2.155.3) (2024-07-12) ### Bug Fixes * serialize jwt as string ([#1657](https://github.com/supabase/auth/issues/1657)) ([98d8324](https://github.com/supabase/auth/commit/98d83245e40d606438eb0afdbf474276179fd91d)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9ad3d484..23e68861c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.155.3](https://github.com/supabase/auth/compare/v2.155.2...v2.155.3) (2024-07-12) + + +### Bug Fixes + +* serialize jwt as string ([#1657](https://github.com/supabase/auth/issues/1657)) ([98d8324](https://github.com/supabase/auth/commit/98d83245e40d606438eb0afdbf474276179fd91d)) + ## [2.155.2](https://github.com/supabase/auth/compare/v2.155.1...v2.155.2) (2024-07-12) From f99286eaed505daf3db6f381265ef6024e7e36d2 Mon Sep 17 00:00:00 2001 From: Stojan Dimitrovski Date: Wed, 17 Jul 2024 17:41:29 +0200 Subject: [PATCH 37/42] fix: treat empty string as nil in `encrypted_password` (#1663) `Authenticate` should treat empty string as nil, as both mean the same thing. --- internal/api/token.go | 4 ++++ internal/models/user.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/internal/api/token.go b/internal/api/token.go index 3362faa93..f8db6f330 100644 --- a/internal/api/token.go +++ b/internal/api/token.go @@ -141,6 +141,10 @@ func (a *API) ResourceOwnerPasswordGrant(ctx context.Context, w http.ResponseWri return internalServerError("Database error querying schema").WithInternalError(err) } + if !user.HasPassword() { + return oauthError("invalid_grant", InvalidLoginMessage) + } + if user.IsBanned() { return oauthError("invalid_grant", InvalidLoginMessage) } diff --git a/internal/models/user.go b/internal/models/user.go index 3c871e543..12ac52816 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -384,6 +384,10 @@ func (u *User) Authenticate(ctx context.Context, tx *storage.Connection, passwor hash := *u.EncryptedPassword + if hash == "" { + return false, false, nil + } + es := crypto.ParseEncryptedString(hash) if es != nil { h, err := es.Decrypt(u.ID.String(), decryptionKeys) From 04752de2d244bb8938c473f5c1ea138e253d3a2a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:21:33 -0700 Subject: [PATCH 38/42] chore(master): release 2.155.4 (#1664) :robot: I have created a release *beep* *boop* --- ## [2.155.4](https://github.com/supabase/auth/compare/v2.155.3...v2.155.4) (2024-07-17) ### Bug Fixes * treat empty string as nil in `encrypted_password` ([#1663](https://github.com/supabase/auth/issues/1663)) ([f99286e](https://github.com/supabase/auth/commit/f99286eaed505daf3db6f381265ef6024e7e36d2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e68861c..7823f9002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.155.4](https://github.com/supabase/auth/compare/v2.155.3...v2.155.4) (2024-07-17) + + +### Bug Fixes + +* treat empty string as nil in `encrypted_password` ([#1663](https://github.com/supabase/auth/issues/1663)) ([f99286e](https://github.com/supabase/auth/commit/f99286eaed505daf3db6f381265ef6024e7e36d2)) + ## [2.155.3](https://github.com/supabase/auth/compare/v2.155.2...v2.155.3) (2024-07-12) From 1858c93bba6f5bc41e4c65489f12c1a0786a1f2b Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 18 Jul 2024 01:15:08 -0700 Subject: [PATCH 39/42] fix: check password max length in checkPasswordStrength (#1659) ## What kind of change does this PR introduce? * Check if password exceeds max length in `checkPasswordStrength` ## What is the current behavior? Please link any relevant issues here. ## What is the new behavior? Feel free to include screenshots if it includes visual changes. ## Additional context Add any other context or screenshots. --- internal/api/admin.go | 5 +++++ internal/api/password.go | 7 +++++++ internal/api/password_test.go | 16 +++++++++++++--- internal/crypto/password.go | 7 ------- internal/models/user_test.go | 13 ------------- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/internal/api/admin.go b/internal/api/admin.go index ecd9c2053..f4b774ec7 100644 --- a/internal/api/admin.go +++ b/internal/api/admin.go @@ -9,11 +9,13 @@ import ( "github.com/fatih/structs" "github.com/go-chi/chi/v5" "github.com/gofrs/uuid" + "github.com/pkg/errors" "github.com/sethvargo/go-password/password" "github.com/supabase/auth/internal/api/provider" "github.com/supabase/auth/internal/models" "github.com/supabase/auth/internal/observability" "github.com/supabase/auth/internal/storage" + "golang.org/x/crypto/bcrypt" ) type AdminUserParams struct { @@ -382,6 +384,9 @@ func (a *API) adminUserCreate(w http.ResponseWriter, r *http.Request) error { } if err != nil { + if errors.Is(err, bcrypt.ErrPasswordTooLong) { + return badRequestError(ErrorCodeValidationFailed, err.Error()) + } return internalServerError("Error creating user").WithInternalError(err) } diff --git a/internal/api/password.go b/internal/api/password.go index 680f396bd..73de368ed 100644 --- a/internal/api/password.go +++ b/internal/api/password.go @@ -8,6 +8,9 @@ import ( "github.com/sirupsen/logrus" ) +// BCrypt hashed passwords have a 72 character limit +const MaxPasswordLength = 72 + // WeakPasswordError encodes an error that a password does not meet strength // requirements. It is handled specially in errors.go as it gets transformed to // a HTTPError with a special weak_password field that encodes the Reasons @@ -24,6 +27,10 @@ func (e *WeakPasswordError) Error() string { func (a *API) checkPasswordStrength(ctx context.Context, password string) error { config := a.config + if len(password) > MaxPasswordLength { + return badRequestError(ErrorCodeValidationFailed, fmt.Sprintf("Password cannot be longer than %v characters", MaxPasswordLength)) + } + var messages, reasons []string if len(password) < config.Password.MinLength { diff --git a/internal/api/password_test.go b/internal/api/password_test.go index 07740440e..f95f6f6d5 100644 --- a/internal/api/password_test.go +++ b/internal/api/password_test.go @@ -85,6 +85,12 @@ func TestPasswordStrengthChecks(t *testing.T) { Password: "abc123", Reasons: nil, }, + { + MinLength: 6, + RequiredCharacters: []string{}, + Password: "zZgXb5gzyCNrV36qwbOSbKVQsVJd28mC1TwRpeB0y6sFNICJyjD6bILKJMsjyKDzBdaY5tmi8zY9BWJYmt3vULLmyafjIDLYjy8qhETu0mS2jj1uQBgSAzJn9Zjm8EFa", + Reasons: nil, + }, } for i, example := range examples { @@ -98,10 +104,14 @@ func TestPasswordStrengthChecks(t *testing.T) { } err := api.checkPasswordStrength(context.Background(), example.Password) - if example.Reasons == nil { + + switch e := err.(type) { + case *WeakPasswordError: + require.Equal(t, e.Reasons, example.Reasons, "Example %d failed with wrong reasons", i) + case *HTTPError: + require.Equal(t, e.ErrorCode, ErrorCodeValidationFailed, "Example %d failed with wrong error code", i) + default: require.NoError(t, err, "Example %d failed with error", i) - } else { - require.Equal(t, err.(*WeakPasswordError).Reasons, example.Reasons, "Example %d failed with wrong reasons", i) } } } diff --git a/internal/crypto/password.go b/internal/crypto/password.go index dca101450..bce145a45 100644 --- a/internal/crypto/password.go +++ b/internal/crypto/password.go @@ -30,9 +30,6 @@ const ( // useful for tests only. QuickHashCost HashCost = iota - // BCrypt hashed passwords have a 72 character limit - MaxPasswordLength = 72 - Argon2Prefix = "$argon2" ) @@ -214,10 +211,6 @@ func CompareHashAndPassword(ctx context.Context, hash, password string) error { func GenerateFromPassword(ctx context.Context, password string) (string, error) { var hashCost int - if len(password) > MaxPasswordLength { - return "", fmt.Errorf("password cannot be longer than %d characters", MaxPasswordLength) - } - switch PasswordHashCost { case QuickHashCost: hashCost = bcrypt.MinCost diff --git a/internal/models/user_test.go b/internal/models/user_test.go index 47d16178d..92b0858ce 100644 --- a/internal/models/user_test.go +++ b/internal/models/user_test.go @@ -2,7 +2,6 @@ package models import ( "context" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -369,18 +368,6 @@ func (ts *UserTestSuite) TestUpdateUserEmailFailure() { require.Equal(ts.T(), primaryIdentity.GetEmail(), userA.GetEmail()) } -func (ts *UserTestSuite) TestSetPasswordTooLong() { - user, err := NewUser("", "", strings.Repeat("a", crypto.MaxPasswordLength), "", nil) - require.NoError(ts.T(), err) - require.NoError(ts.T(), ts.db.Create(user)) - - err = user.SetPassword(ts.db.Context(), strings.Repeat("a", crypto.MaxPasswordLength+1), false, "", "") - require.Error(ts.T(), err) - - err = user.SetPassword(ts.db.Context(), strings.Repeat("a", crypto.MaxPasswordLength), false, "", "") - require.NoError(ts.T(), err) -} - func (ts *UserTestSuite) TestNewUserWithPasswordHashSuccess() { cases := []struct { desc string From 7e67f3edbf81766df297a66f52a8e472583438c6 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 18 Jul 2024 08:02:16 -0700 Subject: [PATCH 40/42] fix: don't update attribute mapping if nil (#1665) --- internal/api/ssoadmin.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/api/ssoadmin.go b/internal/api/ssoadmin.go index 72d88dbac..20fd8b9c5 100644 --- a/internal/api/ssoadmin.go +++ b/internal/api/ssoadmin.go @@ -349,10 +349,13 @@ func (a *API) adminSSOProvidersUpdate(w http.ResponseWriter, r *http.Request) er } } - updateAttributeMapping := !provider.SAMLProvider.AttributeMapping.Equal(¶ms.AttributeMapping) - if updateAttributeMapping { - modified = true - provider.SAMLProvider.AttributeMapping = params.AttributeMapping + updateAttributeMapping := false + if params.AttributeMapping.Keys != nil { + updateAttributeMapping = !provider.SAMLProvider.AttributeMapping.Equal(¶ms.AttributeMapping) + if updateAttributeMapping { + modified = true + provider.SAMLProvider.AttributeMapping = params.AttributeMapping + } } nameIDFormat := "" From 822fb93faab325ba3d4bb628dff43381d68d0b5d Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Fri, 19 Jul 2024 19:41:33 +0200 Subject: [PATCH 41/42] fix: refactor mfa models and add observability to loadFactor (#1669) ## What kind of change does this PR introduce? Makes two changes: - Ensures we track DB calls to loadFactor - Changes the interface for creating a challenge in attempt to make clear that Challenge should always have a Factor --- internal/api/admin.go | 6 ++++-- internal/api/mfa.go | 2 +- internal/api/mfa_test.go | 2 +- internal/models/challenge.go | 11 ----------- internal/models/factor.go | 10 ++++++++++ 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/internal/api/admin.go b/internal/api/admin.go index f4b774ec7..7df41fb65 100644 --- a/internal/api/admin.go +++ b/internal/api/admin.go @@ -70,6 +70,8 @@ func (a *API) loadUser(w http.ResponseWriter, r *http.Request) (context.Context, } func (a *API) loadFactor(w http.ResponseWriter, r *http.Request) (context.Context, error) { + ctx := r.Context() + db := a.db.WithContext(ctx) factorID, err := uuid.FromString(chi.URLParam(r, "factor_id")) if err != nil { return nil, notFoundError(ErrorCodeValidationFailed, "factor_id must be an UUID") @@ -77,14 +79,14 @@ func (a *API) loadFactor(w http.ResponseWriter, r *http.Request) (context.Contex observability.LogEntrySetField(r, "factor_id", factorID) - f, err := models.FindFactorByFactorID(a.db, factorID) + f, err := models.FindFactorByFactorID(db, factorID) if err != nil { if models.IsNotFoundError(err) { return nil, notFoundError(ErrorCodeMFAFactorNotFound, "Factor not found") } return nil, internalServerError("Database error loading factor").WithInternalError(err) } - return withFactor(r.Context(), f), nil + return withFactor(ctx, f), nil } func (a *API) getAdminParams(r *http.Request) (*AdminUserParams, error) { diff --git a/internal/api/mfa.go b/internal/api/mfa.go index df70c6b51..c15f4a3b4 100644 --- a/internal/api/mfa.go +++ b/internal/api/mfa.go @@ -182,7 +182,7 @@ func (a *API) ChallengeFactor(w http.ResponseWriter, r *http.Request) error { user := getUser(ctx) factor := getFactor(ctx) ipAddress := utilities.GetIPAddress(r) - challenge := models.NewChallenge(factor, ipAddress) + challenge := factor.CreateChallenge(ipAddress) if err := db.Transaction(func(tx *storage.Connection) error { if terr := tx.Create(challenge); terr != nil { diff --git a/internal/api/mfa_test.go b/internal/api/mfa_test.go index 63f813249..101f6c660 100644 --- a/internal/api/mfa_test.go +++ b/internal/api/mfa_test.go @@ -265,7 +265,7 @@ func (ts *MFATestSuite) TestMFAVerifyFactor() { req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) testIPAddress := utilities.GetIPAddress(req) - c := models.NewChallenge(f, testIPAddress) + c := f.CreateChallenge(testIPAddress) require.NoError(ts.T(), ts.API.db.Create(c), "Error saving new test challenge") if !v.validChallenge { // Set challenge creation so that it has expired in present time. diff --git a/internal/models/challenge.go b/internal/models/challenge.go index d52132aaa..c088f4b99 100644 --- a/internal/models/challenge.go +++ b/internal/models/challenge.go @@ -22,17 +22,6 @@ func (Challenge) TableName() string { return tableName } -func NewChallenge(factor *Factor, ipAddress string) *Challenge { - id := uuid.Must(uuid.NewV4()) - - challenge := &Challenge{ - ID: id, - FactorID: factor.ID, - IPAddress: ipAddress, - } - return challenge -} - func FindChallengeByID(conn *storage.Connection, challengeID uuid.UUID) (*Challenge, error) { var challenge Challenge err := conn.Find(&challenge, challengeID) diff --git a/internal/models/factor.go b/internal/models/factor.go index 53fddc260..6abd00080 100644 --- a/internal/models/factor.go +++ b/internal/models/factor.go @@ -187,6 +187,16 @@ func DeleteUnverifiedFactors(tx *storage.Connection, user *User) error { return nil } +func (f *Factor) CreateChallenge(ipAddress string) *Challenge { + id := uuid.Must(uuid.NewV4()) + challenge := &Challenge{ + ID: id, + FactorID: f.ID, + IPAddress: ipAddress, + } + return challenge +} + // UpdateFriendlyName changes the friendly name func (f *Factor) UpdateFriendlyName(tx *storage.Connection, friendlyName string) error { f.FriendlyName = friendlyName From ad7a77057e51419f64740b1e05f4cc4ff3fcc2a1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:40:47 -0700 Subject: [PATCH 42/42] chore(master): release 2.155.5 (#1666) :robot: I have created a release *beep* *boop* --- ## [2.155.5](https://github.com/supabase/auth/compare/v2.155.4...v2.155.5) (2024-07-19) ### Bug Fixes * check password max length in checkPasswordStrength ([#1659](https://github.com/supabase/auth/issues/1659)) ([1858c93](https://github.com/supabase/auth/commit/1858c93bba6f5bc41e4c65489f12c1a0786a1f2b)) * don't update attribute mapping if nil ([#1665](https://github.com/supabase/auth/issues/1665)) ([7e67f3e](https://github.com/supabase/auth/commit/7e67f3edbf81766df297a66f52a8e472583438c6)) * refactor mfa models and add observability to loadFactor ([#1669](https://github.com/supabase/auth/issues/1669)) ([822fb93](https://github.com/supabase/auth/commit/822fb93faab325ba3d4bb628dff43381d68d0b5d)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7823f9002..80f34c19b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [2.155.5](https://github.com/supabase/auth/compare/v2.155.4...v2.155.5) (2024-07-19) + + +### Bug Fixes + +* check password max length in checkPasswordStrength ([#1659](https://github.com/supabase/auth/issues/1659)) ([1858c93](https://github.com/supabase/auth/commit/1858c93bba6f5bc41e4c65489f12c1a0786a1f2b)) +* don't update attribute mapping if nil ([#1665](https://github.com/supabase/auth/issues/1665)) ([7e67f3e](https://github.com/supabase/auth/commit/7e67f3edbf81766df297a66f52a8e472583438c6)) +* refactor mfa models and add observability to loadFactor ([#1669](https://github.com/supabase/auth/issues/1669)) ([822fb93](https://github.com/supabase/auth/commit/822fb93faab325ba3d4bb628dff43381d68d0b5d)) + ## [2.155.4](https://github.com/supabase/auth/compare/v2.155.3...v2.155.4) (2024-07-17)