diff --git a/AGENTS.md b/AGENTS.md index 20a3c397..4cf1a69a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ cd ../.. && pnpm test # = playwright test in tests/ - **Config:** global `common.EnvConfig` (caarlos0/env); any secret var supports a `*_FILE` variant. - **Logging:** stdlib `log/slog` only (bridged to OpenTelemetry). No zerolog/logrus in app code. -- `go.mod` pins a **fork** of fosite (`replace github.com/ory/fosite => github.com/pocket-id/fosite`). +- Fosite comes from Pocket ID's **fork** `github.com/pocket-id/fosite`, not `github.com/ory/fosite`. It ports upstream fixes from `ory/hydra`. ## Frontend (SvelteKit) diff --git a/backend/go.mod b/backend/go.mod index ea0ca2b2..cab77e45 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -35,9 +35,9 @@ require ( github.com/lmittmann/tint v1.2.1 github.com/mattn/go-isatty v0.0.24 github.com/mileusna/useragent v1.3.5 - github.com/ory/fosite v0.49.1-0.20250703093431-a5f0b09bf31c github.com/oschwald/maxminddb-golang/v2 v2.7.0 github.com/pires/go-proxyproto v0.15.0 + github.com/pocket-id/fosite v1.4.0 github.com/quic-go/quic-go v0.63.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.12.1 @@ -45,16 +45,16 @@ require ( go.opentelemetry.io/contrib/bridges/otelslog v0.20.1 go.opentelemetry.io/contrib/exporters/autoexport v0.71.0 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.71.0 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0 go.opentelemetry.io/otel v1.47.0 go.opentelemetry.io/otel/sdk v1.47.0 go.opentelemetry.io/otel/sdk/log v0.22.0 go.opentelemetry.io/otel/sdk/metric v1.47.0 go.opentelemetry.io/otel/trace v1.47.0 - golang.org/x/crypto v0.57.0 + golang.org/x/crypto v0.58.0 golang.org/x/image v0.46.0 golang.org/x/sync v0.24.0 - golang.org/x/text v0.42.0 + golang.org/x/text v0.43.0 gorm.io/driver/postgres v1.6.3 gorm.io/gorm v1.31.2 gorm.io/plugin/opentelemetry v0.1.16 @@ -85,7 +85,6 @@ require ( github.com/bytedance/sonic/loader v0.5.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudwego/base64x v0.1.7 // indirect - github.com/cristalhq/jwt/v5 v5.4.0 // indirect github.com/dgraph-io/ristretto/v2 v2.4.2 // indirect github.com/disintegration/gift v1.2.1 // indirect github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d // indirect @@ -121,8 +120,7 @@ require ( github.com/google/go-querystring v1.2.0 // indirect github.com/google/go-tpm v0.9.8 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/websocket v1.5.3 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 // indirect github.com/h2non/filetype v1.1.3 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect @@ -131,7 +129,7 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect - github.com/jaegertracing/jaeger-idl v0.12.0 // indirect + github.com/jaegertracing/jaeger-idl v0.14.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect @@ -186,39 +184,39 @@ require ( go.mongodb.org/mongo-driver/v2 v2.9.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/bridges/prometheus v0.71.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.71.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.46.0 // indirect - go.opentelemetry.io/contrib/propagators/jaeger v1.46.0 // indirect - go.opentelemetry.io/contrib/samplers/jaegerremote v0.37.3 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.72.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.47.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.47.0 // indirect + go.opentelemetry.io/contrib/samplers/jaegerremote v0.38.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.22.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.22.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.46.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.68.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.22.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 // indirect - go.opentelemetry.io/otel/exporters/zipkin v1.46.0 // indirect + go.opentelemetry.io/otel/exporters/zipkin v1.47.0 // indirect go.opentelemetry.io/otel/log v1.47.0 // indirect go.opentelemetry.io/otel/metric v1.47.0 // indirect - go.opentelemetry.io/proto/otlp v1.11.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.1 // indirect go.uber.org/mock v0.6.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/arch v0.31.0 // indirect golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba // indirect - golang.org/x/mod v0.41.0 // indirect - golang.org/x/net v0.59.0 // indirect + golang.org/x/mod v0.42.0 // indirect + golang.org/x/net v0.61.0 // indirect golang.org/x/oauth2 v0.37.0 // indirect - golang.org/x/sys v0.48.0 // indirect + golang.org/x/sys v0.49.0 // indirect golang.org/x/time v0.16.0 // indirect - golang.org/x/tools v0.50.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260918162117-cecb64721679 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 // indirect - google.golang.org/grpc v1.83.2 // indirect + golang.org/x/tools v0.52.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8 // indirect + google.golang.org/grpc v1.84.0 // indirect google.golang.org/protobuf v1.36.12 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 // indirect @@ -226,5 +224,3 @@ require ( modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.12.1 // indirect ) - -replace github.com/ory/fosite => github.com/pocket-id/fosite v1.3.0 diff --git a/backend/go.sum b/backend/go.sum index 0527b414..7fd3e8d9 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -221,10 +221,8 @@ github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFe github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk= 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/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 h1:Bd7KaOxzULLxtZ/K5s1aLbWhR0+5RToO65TXHsf3bqQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0/go.mod h1:nN7ts3dFXKtCZWc//yfkpcQNKJABg16/uDVAZpLDalo= github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg= github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -255,8 +253,8 @@ github.com/jackc/pgx/v5 v5.11.0 h1:IzBBtyK9AHqf98cctWFifYSci2hgQR/cd56wB4p+ogg= github.com/jackc/pgx/v5 v5.11.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jaegertracing/jaeger-idl v0.12.0 h1:FDk3ezIqKk7n9/gzxG9NNjDWzZZLh0GiCxhF4H1LRNU= -github.com/jaegertracing/jaeger-idl v0.12.0/go.mod h1:wWzFftH47XtPRkOM25NPNZ7zBhREWB5HtZBsWj25eW0= +github.com/jaegertracing/jaeger-idl v0.14.0 h1:qfeFswJMftWq2CJKvpws5Zlcpfnnmf9+LmDwTYOUMPo= +github.com/jaegertracing/jaeger-idl v0.14.0/go.mod h1:XGC1/asZXDZTJdN5ZUooZROTlAc6tsbW6sxtF0PNODk= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -394,8 +392,8 @@ github.com/pires/go-proxyproto v0.15.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pocket-id/fosite v1.3.0 h1:lHs3bYhK3Xo7bKYcxSfNWN4hmSLO4gSLsQwwtl4oA0M= -github.com/pocket-id/fosite v1.3.0/go.mod h1:v0FwUcx6Xd7xu/V6hLbIqtFfqRSWcJRJl1f8t0cnIuU= +github.com/pocket-id/fosite v1.4.0 h1:nCCc8rqui1ffUEoH4Xv1XAQZqcXYIUE0Fe4dzLiPh9k= +github.com/pocket-id/fosite v1.4.0/go.mod h1:1zQyPDsHITYiDgzNEVvZcDho56KLb+MWSP6OM3u3s1c= github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= @@ -454,12 +452,12 @@ github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWD github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= -github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= -github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= -github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= -github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/gjson v1.20.0 h1:+agJ3rEzKcCXKDKo0ml26UROcpcAlnZyjq+TjbY5Fto= +github.com/tidwall/gjson v1.20.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= +github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM= +github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.2 h1:dz1jrRuE7or/74V490B4/GP1pZm5WKlt2bgCP5A83w8= +github.com/tidwall/pretty v1.2.2/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= @@ -495,16 +493,16 @@ go.opentelemetry.io/contrib/exporters/autoexport v0.71.0 h1:VCsJbp0YLyPtx2tu5Vgv go.opentelemetry.io/contrib/exporters/autoexport v0.71.0/go.mod h1:qxZqn7e10f6ajmMCkg/47rMS7qQYfaOl2nj/4aytHUQ= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.71.0 h1:TMTU0sQyqsF1QU+/Q4LAZlLOx1L3FJDbk5N2RVB1nx4= go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.71.0/go.mod h1:QzTELfxkj/tFEZSD22OPPwLet5nIPmcdmZPeISk4C8M= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.71.0 h1:oFNJW32h2SXnET7XXstgT7pVh4vN+jW+GfiIaBguIZE= -go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.71.0/go.mod h1:+H3sPOFwag14eMHTPMElZtV0e4YfVZ/85KgrKUCB5FI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU= -go.opentelemetry.io/contrib/propagators/b3 v1.46.0 h1:OFVqWObn7xLIbOjE/koO0LS9fZJNgAyBD0msA+UQAoc= -go.opentelemetry.io/contrib/propagators/b3 v1.46.0/go.mod h1:t/d64xy7xuuEDJN/4ThqohLgRhIuQxL9y7P1v02bYuM= -go.opentelemetry.io/contrib/propagators/jaeger v1.46.0 h1:uxl0SGcmuBkHj/Adl9oftEAyiawQBPL5RzMAmt/Yvq4= -go.opentelemetry.io/contrib/propagators/jaeger v1.46.0/go.mod h1:LiOkxCIvoLofmRps7f8l0NkBtmObnAyQ5trteFs6wj8= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.37.3 h1:20rKrm6q8YlSVwVObflwKd2abo9f8b3WsxJ6YTyQtE8= -go.opentelemetry.io/contrib/samplers/jaegerremote v0.37.3/go.mod h1:2yFxWbgN2VupcMTWu57donCZ9I1lALhawQjNAK7Xmm4= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.72.0 h1:jva1c3z2ZFEQ5sTvnLG2tBALNehO+QvdXKQii6eKRoo= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.72.0/go.mod h1:3pjFS5EnOVfjvYO6/NMdtJFHFQR+8uiGtrZ965nZP4w= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0 h1:LxwW/9ctSCv+QkE/cLR7M91ZIkXNMqJtEMi1vCw9U8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0/go.mod h1:tOsftB4SslBwwErVEPaenU2RpThXWPIU8DoJHEC4dyw= +go.opentelemetry.io/contrib/propagators/b3 v1.47.0 h1:vlKDmtjhe1R0d7kdov/+KVTKWOKeGLyohB+iXoDPRMQ= +go.opentelemetry.io/contrib/propagators/b3 v1.47.0/go.mod h1:0zRiXIfXU30PKYMsdbqYbGK8iHF8KqyPi0qdXT2wAuE= +go.opentelemetry.io/contrib/propagators/jaeger v1.47.0 h1:2lDw6AKPV33/7Qw9oN5YdxuSJIlUz7q3tkfSlJX5rh0= +go.opentelemetry.io/contrib/propagators/jaeger v1.47.0/go.mod h1:y1ORt/QAquBL+O3I06KE7iMENp9p05iBqaV4xNF3J7k= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.38.0 h1:8aH+9AGEhOpbDKahZkwWcxiwoMYH+rlRRz5nD33ttrI= +go.opentelemetry.io/contrib/samplers/jaegerremote v0.38.0/go.mod h1:ZiDcJub/DU2OwJWQrGYhVB9rz6QlGgq4RYgmargUSwY= go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= @@ -517,12 +515,12 @@ go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0 h1:qkD go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.46.0/go.mod h1:tkipS4DRzmpAmvg+Gw4++O1IdDq6TVDnvnYU6cmbQVs= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.46.0 h1:AP23h/mFgb/lc7tdck1Kfn9qxsM8TAeNPCU5C3pzaps= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.46.0/go.mod h1:K4EqCe1b4kGk5WR690ntg9LaBfsPoV32FwthbyoptuA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0 h1:OFnwLJr+pF3iHrlGSzbxyuo6/6HyBlnlN1CWEJmBVcw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.46.0/go.mod h1:716wFneO0ov19A2beH5hjfh9AK5z/VWNAtDijp1Y0/g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 h1:julhjPeUH/q/7hinbSdDdqt5h7Zw9YWmRlWRhI0jd54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0/go.mod h1:Ao2mz688LH/tFf0yMAenidq6k2YNSx6SIY2q6jDACck= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0 h1:w53CDeOA/Kurp7yRsegSr6pbbr759dOvJ+yNmWM6Hxs= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.46.0/go.mod h1:BOmGMCbAtvcJiSJ+hLuhgPLdDbimnraSl8irz3iY8sY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0 h1:KrC1YrQeSt46ITMWAbgQx1M1eV1/1TKzttrBzymPmss= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.46.0/go.mod h1:zDSEzoEqsOrgBeGvH66KRgxh90VonFyJqBHA0Pk3+rM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0 h1:aXZXsZ012wOgVkqaiQv+Z/d8V0xKjmg70F6m4CE94lc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0/go.mod h1:ziu1gUIJhAaxM3EV1e3Ralk5AAyL1UOYz3lJJ4VSI38= go.opentelemetry.io/otel/exporters/prometheus v0.68.0 h1:QOf2IftqQwITVRJpnn0M7M9ZCbgWfxz4P7i9C9yc2N4= go.opentelemetry.io/otel/exporters/prometheus v0.68.0/go.mod h1:bgSvqu2TWGXiz7yr5UTMfObH8oqxJWHTnubQ3ef9BO4= go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.22.0 h1:kvMAiLEudKmk+CSG+iYbU8vTUGNNDaf/V09OO5lrTwI= @@ -531,8 +529,8 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0 h1:PR9eAf7o0dQs3h go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.46.0/go.mod h1:2Z4KyNdH1uuzivdinyfGsxzNNT/Rl45pwtVwfYVI0xk= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0 h1:KdRxPiAoMptR3vfWzvjjvutTsSiwbC2uG0496rzZNfo= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.46.0/go.mod h1:K/qSA+3G7Eovxi4K09wzrAgkWRnosS0DAOZeEpve7sM= -go.opentelemetry.io/otel/exporters/zipkin v1.46.0 h1:7y0nqfbwuPdaYKwm35PRMRMOa8iYu1SXxnAJNNR2o1M= -go.opentelemetry.io/otel/exporters/zipkin v1.46.0/go.mod h1:MGmDLXGsdDzWBOt0y5VcW2u5hRsFV4MzylPvvNkQ9qw= +go.opentelemetry.io/otel/exporters/zipkin v1.47.0 h1:4oTg347RRFG5HYiDFr6vnzj3BzsT5BNnSB1n6a4wAcg= +go.opentelemetry.io/otel/exporters/zipkin v1.47.0/go.mod h1:nZy5oQ8jLItEWTbifyWwwfcOSf/8wLabOwq+PI0oAKg= go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= @@ -549,8 +547,8 @@ go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= -go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= -go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= +go.opentelemetry.io/proto/otlp v1.11.1 h1:SCrPqH9NE5CmBrOqpjNCiT0X5mGwMyGERXdogfR7Yjw= +go.opentelemetry.io/proto/otlp v1.11.1/go.mod h1:wq8W4aSf6bOy8RZyESOeQFr9Pu7wydWX/TY4sgvrTPg= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= @@ -571,8 +569,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= -golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= +golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54= +golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w= golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba h1:Ck8QetSgk912qxWLMCKxd0in+aiyBQyDSMae6e/xmpU= golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba/go.mod h1:50RgIsmK7OwqzTTeqcSXQW8SswW0o8fRcDxmqGluJ8E= golang.org/x/image v0.46.0 h1:b1+oYj0Jbp6K5MDT4i4/eZpYlk3V8SJhhDKh6LBHAyQ= @@ -585,8 +583,8 @@ golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= -golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= +golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg= +golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -606,8 +604,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues= -golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= +golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw= +golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98= golang.org/x/oauth2 v0.37.0/go.mod h1:IxwZNxUULJmpBFf9K/9NTMSIfZZuvuTy1gGxhigP/58= @@ -637,8 +635,8 @@ golang.org/x/sys v0.12.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/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= -golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= +golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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= @@ -659,8 +657,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= -golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE= golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -672,8 +670,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU= -golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0= +golang.org/x/tools v0.52.0 h1:ryxg4U63oaUYk7uOLTF37n43f4qgtGvqV4Ioit1BaEs= +golang.org/x/tools v0.52.0/go.mod h1:ItcP5UNaVGG+Yc4eIYmaFLNJ4soEopkx5Y2d4lcEurs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -681,12 +679,12 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto/googleapis/api v0.0.0-20260918162117-cecb64721679 h1:FEp7JNE32DTAwbnI/ixagnmj7Xm1eTONofGEUXFjZ4w= -google.golang.org/genproto/googleapis/api v0.0.0-20260918162117-cecb64721679/go.mod h1:52bV8FLAQ9Qmcqaq9ECLmuEHZthk+6OPV45aKBBrsNw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 h1:KmqdJU4vrNcxy/6qdg3JduZtalEXrJLspVltnR1cE+8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= +google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8 h1:cFbkNigDInw+wVTXc+CyhUsoHGJCw0BR6mlNoRVfre0= +google.golang.org/genproto/googleapis/api v0.0.0-20261005182115-fad411399dd8/go.mod h1:QK6p7IgD7IZEZJBhhSUCib5X2vMOzlrGCGUw2XyVDh0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8 h1:0SXBqli5dnuCKMjI/X3FM226zEaEa0GUcSCJtN1vL2o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261005182115-fad411399dd8/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/backend/internal/api/service.go b/backend/internal/api/service.go index 24ccb815..1e01e224 100644 --- a/backend/internal/api/service.go +++ b/backend/internal/api/service.go @@ -9,7 +9,7 @@ import ( "strings" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/apperror" "github.com/pocket-id/pocket-id/backend/internal/model" datatype "github.com/pocket-id/pocket-id/backend/internal/model/types" diff --git a/backend/internal/authz/scope_test.go b/backend/internal/authz/scope_test.go index 370d8934..341b0bd0 100644 --- a/backend/internal/authz/scope_test.go +++ b/backend/internal/authz/scope_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/require" ) diff --git a/backend/internal/dto/validations.go b/backend/internal/dto/validations.go index 3265c384..4af67d44 100644 --- a/backend/internal/dto/validations.go +++ b/backend/internal/dto/validations.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/model" "github.com/pocket-id/pocket-id/backend/internal/utils" diff --git a/backend/internal/oidc/access_token_scope.go b/backend/internal/oidc/access_token_scope.go index e9260024..e7d74a9e 100644 --- a/backend/internal/oidc/access_token_scope.go +++ b/backend/internal/oidc/access_token_scope.go @@ -4,15 +4,15 @@ import ( "context" "slices" - "github.com/ory/fosite" - fositeoauth2 "github.com/ory/fosite/handler/oauth2" + "github.com/pocket-id/fosite" + fositeoauth2 "github.com/pocket-id/fosite/handler/oauth2" ) // NewAccessTokenStrategy applies Pocket ID's identity-audience policy to the provided access token strategy -func NewAccessTokenStrategy(coreStrategy fositeoauth2.CoreStrategy, issuer string) fositeoauth2.CoreStrategy { +func NewAccessTokenStrategy(accessTokenStrategy fositeoauth2.AccessTokenStrategy, issuer string) fositeoauth2.AccessTokenStrategy { return identityAudienceAccessTokenStrategy{ - CoreStrategy: coreStrategy, - issuer: issuer, + AccessTokenStrategy: accessTokenStrategy, + issuer: issuer, } } @@ -58,10 +58,10 @@ func (r identityAudienceRequester) GetGrantedAudience() fosite.Arguments { // identityAudienceAccessTokenStrategy wraps the access token strategy so an access token granted an identity scope also lists the issuer in its audience, keeping the self-contained JWT consistent with what is persisted for introspection and userinfo type identityAudienceAccessTokenStrategy struct { - fositeoauth2.CoreStrategy + fositeoauth2.AccessTokenStrategy issuer string } func (s identityAudienceAccessTokenStrategy) GenerateAccessToken(ctx context.Context, requester fosite.Requester) (string, string, error) { - return s.CoreStrategy.GenerateAccessToken(ctx, withIdentityAudience(requester, s.issuer)) + return s.AccessTokenStrategy.GenerateAccessToken(ctx, withIdentityAudience(requester, s.issuer)) } diff --git a/backend/internal/oidc/access_token_scope_test.go b/backend/internal/oidc/access_token_scope_test.go index cd2d716a..e9a18c90 100644 --- a/backend/internal/oidc/access_token_scope_test.go +++ b/backend/internal/oidc/access_token_scope_test.go @@ -3,7 +3,7 @@ package oidc import ( "testing" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/assert" "github.com/pocket-id/pocket-id/backend/internal/model" diff --git a/backend/internal/oidc/api_resource.go b/backend/internal/oidc/api_resource.go index 75136edb..cb3ac061 100644 --- a/backend/internal/oidc/api_resource.go +++ b/backend/internal/oidc/api_resource.go @@ -5,7 +5,7 @@ import ( "slices" "strings" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/dto" "gorm.io/gorm" ) diff --git a/backend/internal/oidc/authorization_handler.go b/backend/internal/oidc/authorization_handler.go index 469c2989..554fe81b 100644 --- a/backend/internal/oidc/authorization_handler.go +++ b/backend/internal/oidc/authorization_handler.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/authz" "github.com/pocket-id/pocket-id/backend/internal/httpserver" "github.com/pocket-id/pocket-id/backend/internal/utils" diff --git a/backend/internal/oidc/authorization_service.go b/backend/internal/oidc/authorization_service.go index a5f9bc29..1cf16003 100644 --- a/backend/internal/oidc/authorization_service.go +++ b/backend/internal/oidc/authorization_service.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "gorm.io/gorm" "github.com/pocket-id/pocket-id/backend/internal/apperror" diff --git a/backend/internal/oidc/authorization_service_test.go b/backend/internal/oidc/authorization_service_test.go index 3f4f1a78..d3d10a55 100644 --- a/backend/internal/oidc/authorization_service_test.go +++ b/backend/internal/oidc/authorization_service_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/require" "gorm.io/gorm" diff --git a/backend/internal/oidc/cimd.go b/backend/internal/oidc/cimd.go index 5a6463cc..8d6bd908 100644 --- a/backend/internal/oidc/cimd.go +++ b/backend/internal/oidc/cimd.go @@ -9,7 +9,7 @@ import ( "slices" "strings" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/model" datatype "github.com/pocket-id/pocket-id/backend/internal/model/types" diff --git a/backend/internal/oidc/cimd_test.go b/backend/internal/oidc/cimd_test.go index a4ebeb5e..08fd5d63 100644 --- a/backend/internal/oidc/cimd_test.go +++ b/backend/internal/oidc/cimd_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" diff --git a/backend/internal/oidc/claims_service.go b/backend/internal/oidc/claims_service.go index ef8f2fd7..f284fa64 100644 --- a/backend/internal/oidc/claims_service.go +++ b/backend/internal/oidc/claims_service.go @@ -6,7 +6,7 @@ import ( "errors" "slices" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/common" "github.com/pocket-id/pocket-id/backend/internal/model" "gorm.io/gorm" diff --git a/backend/internal/oidc/claims_service_test.go b/backend/internal/oidc/claims_service_test.go index 78d9fbba..194b8cfc 100644 --- a/backend/internal/oidc/claims_service_test.go +++ b/backend/internal/oidc/claims_service_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/lestrrat-go/jwx/v4/jwa" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/require" "gorm.io/gorm" diff --git a/backend/internal/oidc/client.go b/backend/internal/oidc/client.go index 9fc57466..b53bdec9 100644 --- a/backend/internal/oidc/client.go +++ b/backend/internal/oidc/client.go @@ -4,7 +4,7 @@ import ( "slices" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/model" ) diff --git a/backend/internal/oidc/client_test.go b/backend/internal/oidc/client_test.go index 1f23da50..a296fc0d 100644 --- a/backend/internal/oidc/client_test.go +++ b/backend/internal/oidc/client_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/backend/internal/oidc/device_handler.go b/backend/internal/oidc/device_handler.go index fae00add..330ef4bb 100644 --- a/backend/internal/oidc/device_handler.go +++ b/backend/internal/oidc/device_handler.go @@ -6,7 +6,7 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/apperror" "github.com/pocket-id/pocket-id/backend/internal/authz" "github.com/pocket-id/pocket-id/backend/internal/utils/cookie" diff --git a/backend/internal/oidc/device_openid_session_test.go b/backend/internal/oidc/device_openid_session_test.go new file mode 100644 index 00000000..40576daa --- /dev/null +++ b/backend/internal/oidc/device_openid_session_test.go @@ -0,0 +1,82 @@ +package oidc + +import ( + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" + + "github.com/pocket-id/fosite" + "github.com/stretchr/testify/require" +) + +func TestDeviceCodeExchangeConsumesOpenIDConnectSession(t *testing.T) { + const ( + userID = "test-user" + clientID = "test-client" + ) + service, store, provider, userCode, deviceCode := newTestDeviceServiceWithCode(t, clientID, userID, false, nil) + require.NoError(t, service.acceptDeviceCode(t.Context(), userCode, userID, "phr", time.Now().UTC(), "", requestMeta{})) + + signature, err := provider.deviceStrategy.DeviceCodeSignature(t.Context(), deviceCode) + require.NoError(t, err) + + // The approved authorization keeps an OpenID Connect session that expires together with the device code + var deviceSession, openIDSession OAuth2Session + require.NoError(t, store.db.First(&deviceSession, "kind = ? AND key = ?", sessionKindDeviceCode, signature).Error) + require.NoError(t, store.db.First(&openIDSession, "kind = ? AND key = ?", sessionKindOpenID, signature).Error) + require.NotNil(t, deviceSession.ExpiresAt) + require.NotNil(t, openIDSession.ExpiresAt) + require.True(t, deviceSession.ExpiresAt.ToTime().Equal(openIDSession.ExpiresAt.ToTime())) + + // The token exchange issues an ID token and consumes the OpenID Connect session + response, err := exchangeDeviceCode(t, provider, clientID, deviceCode) + require.NoError(t, err) + require.NotNil(t, response.GetExtra("id_token")) + + var count int64 + require.NoError(t, store.db.Model(&OAuth2Session{}).Where("kind = ?", sessionKindOpenID).Count(&count).Error) + require.Zero(t, count) + require.NoError(t, store.db.Model(&OAuth2Session{}).Where("kind = ? AND request_id = ?", sessionKindAccessToken, deviceSession.RequestID).Count(&count).Error) + require.EqualValues(t, 1, count) + + // A replayed device code is rejected before an OpenID Connect session could be reused + _, err = exchangeDeviceCode(t, provider, clientID, deviceCode) + require.ErrorIs(t, err, fosite.ErrInvalidGrant) + + // The replay revokes the access token that the first exchange issued + require.NoError(t, store.db.Model(&OAuth2Session{}).Where("kind = ? AND request_id = ?", sessionKindAccessToken, deviceSession.RequestID).Count(&count).Error) + require.Zero(t, count) +} + +func TestStoreInvalidateDeviceCodeSessionReportsLostRaceAsInvalidGrant(t *testing.T) { + _, store, provider, _, deviceCode := newTestDeviceServiceWithCode(t, "test-client", "test-user", false, nil) + + signature, err := provider.deviceStrategy.DeviceCodeSignature(t.Context(), deviceCode) + require.NoError(t, err) + + // The first redemption wins and a concurrent second one must not mint another token set + require.NoError(t, store.InvalidateDeviceCodeSession(t.Context(), signature)) + require.ErrorIs(t, store.InvalidateDeviceCodeSession(t.Context(), signature), fosite.ErrInvalidGrant) +} + +// exchangeDeviceCode redeems the device code at the token endpoint like a polling device would +func exchangeDeviceCode(t *testing.T, provider *oidcProvider, clientID, deviceCode string) (fosite.AccessResponder, error) { + t.Helper() + + form := url.Values{ + "grant_type": {string(fosite.GrantTypeDeviceCode)}, + "device_code": {deviceCode}, + "client_id": {clientID}, + } + req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/api/oidc/token", strings.NewReader(form.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + accessRequest, err := provider.NewAccessRequest(t.Context(), req, NewEmptySession()) + if err != nil { + return nil, err + } + return provider.NewAccessResponse(t.Context(), accessRequest) +} diff --git a/backend/internal/oidc/device_service.go b/backend/internal/oidc/device_service.go index 75b9ab57..598689b5 100644 --- a/backend/internal/oidc/device_service.go +++ b/backend/internal/oidc/device_service.go @@ -7,8 +7,8 @@ import ( "strings" "time" - "github.com/ory/fosite" - "github.com/ory/fosite/handler/rfc8628" + "github.com/pocket-id/fosite" + "github.com/pocket-id/fosite/handler/rfc8628" "gorm.io/gorm" "github.com/pocket-id/pocket-id/backend/internal/apperror" @@ -135,6 +135,9 @@ func (s *deviceService) acceptDeviceCode(ctx context.Context, userCode, userID, session := NewAuthenticatedSession(userID, authenticationMethod, authenticationTime, request.GetRequestedAt()) + // Keep the lifetimes of the pending device authorization, so its codes and the OpenID Connect session created below still expire + copyDeviceCodeExpiry(request.GetSession(), session) + if err = s.claimsService.applyIDTokenClaims(ctx, session, request.GetGrantedScopes()); err != nil { return err } @@ -166,6 +169,18 @@ func (s *deviceService) acceptDeviceCode(ctx context.Context, userCode, userID, }) } +// copyDeviceCodeExpiry copies the device and user code expiry from the pending device authorization's session to the session that replaces it +func copyDeviceCodeExpiry(from fosite.Session, to *Session) { + if from == nil { + return + } + for _, tokenType := range []fosite.TokenType{fosite.DeviceCode, fosite.UserCode} { + if expiresAt := from.GetExpiresAt(tokenType); !expiresAt.IsZero() { + to.SetExpiresAt(tokenType, expiresAt) + } + } +} + func (s *deviceService) loadDeviceAuthorizationUser(ctx context.Context, userID string) (model.User, error) { tx := s.db.Begin() defer func() { diff --git a/backend/internal/oidc/device_service_test.go b/backend/internal/oidc/device_service_test.go index 15db3b53..a4279c7a 100644 --- a/backend/internal/oidc/device_service_test.go +++ b/backend/internal/oidc/device_service_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/apperror" "github.com/pocket-id/pocket-id/backend/internal/model" testutils "github.com/pocket-id/pocket-id/backend/internal/utils/testing" diff --git a/backend/internal/oidc/device_strategy.go b/backend/internal/oidc/device_strategy.go index be00463e..9827b286 100644 --- a/backend/internal/oidc/device_strategy.go +++ b/backend/internal/oidc/device_strategy.go @@ -3,7 +3,7 @@ package oidc import ( "context" - "github.com/ory/fosite/handler/rfc8628" + "github.com/pocket-id/fosite/handler/rfc8628" "github.com/pocket-id/pocket-id/backend/internal/utils" ) diff --git a/backend/internal/oidc/federated_client_auth.go b/backend/internal/oidc/federated_client_auth.go index 1c75cf21..a4bca6d4 100644 --- a/backend/internal/oidc/federated_client_auth.go +++ b/backend/internal/oidc/federated_client_auth.go @@ -17,7 +17,7 @@ import ( "github.com/lestrrat-go/jwx/v4/jwk" "github.com/lestrrat-go/jwx/v4/jws" "github.com/lestrrat-go/jwx/v4/jwt" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/model" jwkutils "github.com/pocket-id/pocket-id/backend/internal/utils/jwk" diff --git a/backend/internal/oidc/federated_client_auth_test.go b/backend/internal/oidc/federated_client_auth_test.go index 1cd14b9d..3f65b66a 100644 --- a/backend/internal/oidc/federated_client_auth_test.go +++ b/backend/internal/oidc/federated_client_auth_test.go @@ -14,7 +14,7 @@ import ( "github.com/lestrrat-go/jwx/v4/jwa" "github.com/lestrrat-go/jwx/v4/jwk" "github.com/lestrrat-go/jwx/v4/jwt" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/require" "github.com/pocket-id/pocket-id/backend/internal/model" diff --git a/backend/internal/oidc/introspection_handler.go b/backend/internal/oidc/introspection_handler.go index 3f1e4eff..9e2e5d82 100644 --- a/backend/internal/oidc/introspection_handler.go +++ b/backend/internal/oidc/introspection_handler.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/gin-gonic/gin" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" ) type introspectionHandler struct { diff --git a/backend/internal/oidc/introspection_handler_test.go b/backend/internal/oidc/introspection_handler_test.go index fd0bcc9a..7f855675 100644 --- a/backend/internal/oidc/introspection_handler_test.go +++ b/backend/internal/oidc/introspection_handler_test.go @@ -16,7 +16,7 @@ import ( "github.com/lestrrat-go/jwx/v4/jwa" "github.com/lestrrat-go/jwx/v4/jwk" "github.com/lestrrat-go/jwx/v4/jwt" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/require" "github.com/pocket-id/pocket-id/backend/internal/model" diff --git a/backend/internal/oidc/par_handler.go b/backend/internal/oidc/par_handler.go index adf37d10..8049187e 100644 --- a/backend/internal/oidc/par_handler.go +++ b/backend/internal/oidc/par_handler.go @@ -4,7 +4,7 @@ import ( "log/slog" "github.com/gin-gonic/gin" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" ) type parHandler struct { diff --git a/backend/internal/oidc/preview.go b/backend/internal/oidc/preview.go index 7f4313b3..ca3af97c 100644 --- a/backend/internal/oidc/preview.go +++ b/backend/internal/oidc/preview.go @@ -7,7 +7,7 @@ import ( "time" jwxjwt "github.com/lestrrat-go/jwx/v4/jwt" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/pocket-id/pocket-id/backend/internal/model" "github.com/pocket-id/pocket-id/backend/internal/utils" diff --git a/backend/internal/oidc/provider.go b/backend/internal/oidc/provider.go index 34653f46..9408918b 100644 --- a/backend/internal/oidc/provider.go +++ b/backend/internal/oidc/provider.go @@ -8,11 +8,11 @@ import ( "net/url" "time" - "github.com/ory/fosite" - "github.com/ory/fosite/compose" - fositeoauth2 "github.com/ory/fosite/handler/oauth2" - "github.com/ory/fosite/handler/openid" - "github.com/ory/fosite/token/jwt" + "github.com/pocket-id/fosite" + "github.com/pocket-id/fosite/compose" + fositeoauth2 "github.com/pocket-id/fosite/handler/oauth2" + "github.com/pocket-id/fosite/handler/openid" + "github.com/pocket-id/fosite/token/jwt" "github.com/pocket-id/pocket-id/backend/internal/utils" "golang.org/x/crypto/hkdf" ) @@ -65,15 +65,12 @@ func newProvider(store *Store, authenticator *federatedClientAuthenticator, sign return SigningKeyFromSigner(signer) } sig := newJWTSigner(keyGetter) - coreStrategy := compose.NewOAuth2HMACStrategy(fositeConfig) deviceStrategy := &deviceStrategy{DefaultDeviceStrategy: compose.NewDeviceStrategy(fositeConfig)} - defaultAccessTokenStrategy := &fositeoauth2.DefaultJWTStrategy{ - Signer: sig, - HMACSHAStrategy: coreStrategy, - Config: fositeConfig, - } rfc9068AccessTokenStrategy := &fositeoauth2.RFC9068JWTStrategy{ - DefaultJWTStrategy: defaultAccessTokenStrategy, + DefaultJWTStrategy: &fositeoauth2.DefaultJWTStrategy{ + Signer: sig, + Config: fositeConfig, + }, } // Apply Pocket ID's identity-audience policy outside Fosite's reusable RFC 9068 token profile @@ -85,11 +82,12 @@ func newProvider(store *Store, authenticator *federatedClientAuthenticator, sign provider := compose.Compose( fositeConfig, store, - &compose.CommonStrategy{ - CoreStrategy: accessTokenStrategy, - RFC8628CodeStrategy: deviceStrategy, - OpenIDConnectTokenStrategy: idTokenStrategy, - Signer: sig, + &strategyProvider{ + opaque: compose.NewOAuth2HMACStrategy(fositeConfig), + accessToken: accessTokenStrategy, + device: deviceStrategy, + idToken: idTokenStrategy, + Signer: sig, }, compose.OAuth2AuthorizeExplicitFactory, compose.OAuth2ClientCredentialsGrantFactory, diff --git a/backend/internal/oidc/provider_test.go b/backend/internal/oidc/provider_test.go index e35ae960..04800b58 100644 --- a/backend/internal/oidc/provider_test.go +++ b/backend/internal/oidc/provider_test.go @@ -17,8 +17,8 @@ import ( "time" "github.com/lestrrat-go/jwx/v4/jwa" - "github.com/ory/fosite" - fositeoauth2 "github.com/ory/fosite/handler/oauth2" + "github.com/pocket-id/fosite" + fositeoauth2 "github.com/pocket-id/fosite/handler/oauth2" "github.com/pocket-id/pocket-id/backend/internal/model" datatype "github.com/pocket-id/pocket-id/backend/internal/model/types" testutils "github.com/pocket-id/pocket-id/backend/internal/utils/testing" diff --git a/backend/internal/oidc/refresh_token_test.go b/backend/internal/oidc/refresh_token_test.go index 09699499..03a2f478 100644 --- a/backend/internal/oidc/refresh_token_test.go +++ b/backend/internal/oidc/refresh_token_test.go @@ -13,8 +13,8 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/ory/fosite" - "github.com/ory/fosite/compose" + "github.com/pocket-id/fosite" + "github.com/pocket-id/fosite/compose" "github.com/stretchr/testify/require" "gorm.io/gorm" diff --git a/backend/internal/oidc/session.go b/backend/internal/oidc/session.go index e02e9881..aec212ed 100644 --- a/backend/internal/oidc/session.go +++ b/backend/internal/oidc/session.go @@ -5,10 +5,10 @@ import ( "time" "uuid" - "github.com/ory/fosite" - fositeoauth2 "github.com/ory/fosite/handler/oauth2" - "github.com/ory/fosite/handler/openid" - fositejwt "github.com/ory/fosite/token/jwt" + "github.com/pocket-id/fosite" + fositeoauth2 "github.com/pocket-id/fosite/handler/oauth2" + "github.com/pocket-id/fosite/handler/openid" + fositejwt "github.com/pocket-id/fosite/token/jwt" ) var _ openid.Session = (*Session)(nil) diff --git a/backend/internal/oidc/signer.go b/backend/internal/oidc/signer.go index 3d4d0ce6..f0408c42 100644 --- a/backend/internal/oidc/signer.go +++ b/backend/internal/oidc/signer.go @@ -5,7 +5,7 @@ import ( "errors" jose "github.com/go-jose/go-jose/v4" - fositejwt "github.com/ory/fosite/token/jwt" + fositejwt "github.com/pocket-id/fosite/token/jwt" ) // SigningKeyFromSigner wraps the raw signing key in a *jose.JSONWebKey that carries the diff --git a/backend/internal/oidc/store.go b/backend/internal/oidc/store.go index ef06f2e0..2d4e6b45 100644 --- a/backend/internal/oidc/store.go +++ b/backend/internal/oidc/store.go @@ -10,12 +10,11 @@ import ( "slices" "time" - "github.com/ory/fosite" - fositeoauth2 "github.com/ory/fosite/handler/oauth2" - "github.com/ory/fosite/handler/openid" - "github.com/ory/fosite/handler/pkce" - "github.com/ory/fosite/handler/rfc8628" - fositestorage "github.com/ory/fosite/storage" + "github.com/pocket-id/fosite" + fositeoauth2 "github.com/pocket-id/fosite/handler/oauth2" + "github.com/pocket-id/fosite/handler/openid" + "github.com/pocket-id/fosite/handler/pkce" + "github.com/pocket-id/fosite/handler/rfc8628" "github.com/pocket-id/pocket-id/backend/internal/model" datatype "github.com/pocket-id/pocket-id/backend/internal/model/types" "gorm.io/gorm" @@ -35,14 +34,17 @@ const ( ) var ( - _ fosite.Storage = (*Store)(nil) - _ fosite.PARStorage = (*Store)(nil) - _ fositeoauth2.CoreStorage = (*Store)(nil) - _ fositeoauth2.TokenRevocationStorage = (*Store)(nil) - _ rfc8628.RFC8628CoreStorage = (*Store)(nil) - _ openid.OpenIDConnectRequestStorage = (*Store)(nil) - _ pkce.PKCERequestStorage = (*Store)(nil) - _ fositestorage.Transactional = (*Store)(nil) + _ fosite.Storage = (*Store)(nil) + _ fosite.ClientManager = (*Store)(nil) + _ fosite.PARStorageProvider = (*Store)(nil) + _ fositeoauth2.AuthorizeCodeStorageProvider = (*Store)(nil) + _ fositeoauth2.AccessTokenStorageProvider = (*Store)(nil) + _ fositeoauth2.RefreshTokenStorageProvider = (*Store)(nil) + _ fositeoauth2.TokenRevocationStorageProvider = (*Store)(nil) + _ rfc8628.DeviceAuthStorageProvider = (*Store)(nil) + _ openid.OpenIDConnectRequestStorageProvider = (*Store)(nil) + _ pkce.PKCERequestStorageProvider = (*Store)(nil) + _ fosite.Transactional = (*Store)(nil) ) // NewStore creates the fosite storage. Exported for packages that need to seed or @@ -88,7 +90,7 @@ type storedRequester struct { DefaultResponseMode fosite.ResponseModeType `json:"default_response_mode,omitempty"` } -// Satisfies fosite.Storage +// Satisfies fosite.ClientManager func (s *Store) GetClient(ctx context.Context, id string) (fosite.Client, error) { tx := s.dbFor(ctx) @@ -324,14 +326,14 @@ func (s *Store) firstClientByID(ctx context.Context, id string) (model.OidcClien return client, nil } -// Satisfies fositeoauth2.CoreStorage +// Satisfies fositeoauth2.AuthorizeCodeStorage, fositeoauth2.AccessTokenStorage and fositeoauth2.RefreshTokenStorage -func (s *Store) CreateAuthorizeCodeSession(ctx context.Context, code string, request fosite.Requester) error { - return s.upsertSession(ctx, sessionKindAuthorizeCode, code, request, "", true, fosite.AuthorizeCode) +func (s *Store) CreateAuthorizeCodeSession(ctx context.Context, signature string, request fosite.Requester) error { + return s.upsertSession(ctx, sessionKindAuthorizeCode, signature, request, "", true, fosite.AuthorizeCode) } -func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, _ fosite.Session) (fosite.Requester, error) { - request, active, err := s.getRequesterSession(ctx, sessionKindAuthorizeCode, code) +func (s *Store) GetAuthorizeCodeSession(ctx context.Context, _ string, signature string, _ fosite.Session) (fosite.Requester, error) { + request, active, err := s.getRequesterSession(ctx, sessionKindAuthorizeCode, signature) if err != nil { return nil, err } @@ -341,8 +343,14 @@ func (s *Store) GetAuthorizeCodeSession(ctx context.Context, code string, _ fosi return request, nil } -func (s *Store) InvalidateAuthorizeCodeSession(ctx context.Context, code string) error { - return s.deactivateSession(ctx, sessionKindAuthorizeCode, code) +func (s *Store) InvalidateAuthorizeCodeSession(ctx context.Context, _ string, signature string) error { + err := s.deactivateSession(ctx, sessionKindAuthorizeCode, signature) + + // The code was active when the token request read it, so a missing active row means a concurrent request redeemed it first + if errors.Is(err, fosite.ErrNotFound) { + return fosite.ErrInvalidGrant.WithHint("The authorization code has already been used.") + } + return err } func (s *Store) CreateAccessTokenSession(ctx context.Context, signature string, request fosite.Requester) error { @@ -524,19 +532,25 @@ func (s *Store) CreatePKCERequestSession(ctx context.Context, signature string, return s.upsertSession(ctx, sessionKindPKCE, signature, requester, "", true, fosite.AuthorizeCode) } -func (s *Store) GetPKCERequestSession(ctx context.Context, signature string, _ fosite.Session) (fosite.Requester, error) { +func (s *Store) GetPKCERequestSession(ctx context.Context, _ string, signature string, _ fosite.Session) (fosite.Requester, error) { request, _, err := s.getRequesterSession(ctx, sessionKindPKCE, signature) return request, err } -func (s *Store) DeletePKCERequestSession(ctx context.Context, signature string) error { +func (s *Store) DeletePKCERequestSession(ctx context.Context, _ string, signature string) error { return s.deleteSession(ctx, sessionKindPKCE, signature) } // Satisfies openid.OpenIDConnectRequestStorage func (s *Store) CreateOpenIDConnectSession(ctx context.Context, authorizeCode string, requester fosite.Requester) error { - return s.upsertSession(ctx, sessionKindOpenID, authorizeCode, requester, "", true, fosite.AuthorizeCode) + // Sessions of the device authorization grant are keyed by the device code signature and expire together with the device code + // An expiry ensures the cleanup job removes sessions whose code is never redeemed + expiresAtKey := fosite.AuthorizeCode + if session := requester.GetSession(); session != nil && session.GetExpiresAt(fosite.AuthorizeCode).IsZero() { + expiresAtKey = fosite.DeviceCode + } + return s.upsertSession(ctx, sessionKindOpenID, authorizeCode, requester, "", true, expiresAtKey) } func (s *Store) GetOpenIDConnectSession(ctx context.Context, authorizeCode string, _ fosite.Requester) (fosite.Requester, error) { @@ -584,7 +598,7 @@ func (s *Store) DeletePARSession(ctx context.Context, requestURI string) error { return s.deleteSession(ctx, sessionKindPAR, requestURI) } -// Satisfies rfc8628.RFC8628CoreStorage +// Satisfies rfc8628.DeviceAuthStorage func (s *Store) CreateDeviceAuthSession(ctx context.Context, deviceCodeSignature, userCodeSignature string, request fosite.DeviceRequester) error { requestData, err := s.encodeDeviceRequester(request) @@ -633,7 +647,8 @@ func (s *Store) InvalidateDeviceCodeSession(ctx context.Context, signature strin return result.Error } if result.RowsAffected == 0 { - return fosite.ErrNotFound + // The device code was active when the token request read it, so a concurrent request redeemed it first + return fosite.ErrInvalidGrant.WithHint("The device code has already been used.") } return nil } @@ -709,30 +724,9 @@ func (s *Store) AcceptDeviceCodeSessionByUserCodeSignature(ctx context.Context, return deviceCodeSignature, err } -// Satisfies fositestorage.Transactional - -func (s *Store) BeginTX(ctx context.Context) (context.Context, error) { - tx := s.db.WithContext(ctx).Begin() - if tx.Error != nil { - return ctx, tx.Error - } - return contextWithTx(ctx, tx), nil -} - -func (s *Store) Commit(ctx context.Context) error { - tx, ok := ctx.Value(txContextKey{}).(*gorm.DB) - if !ok { - return nil - } - return tx.Commit().Error -} - -func (s *Store) Rollback(ctx context.Context) error { - tx, ok := ctx.Value(txContextKey{}).(*gorm.DB) - if !ok { - return nil - } - return tx.Rollback().Error +// Satisfies fosite.Transactional +func (s *Store) Transaction(ctx context.Context, fn func(ctx context.Context) error) error { + return withTx(ctx, s.db, fn) } func (s *Store) upsertSession(ctx context.Context, kind string, key string, requester fosite.Requester, accessTokenSignature string, active bool, expiresAtKey fosite.TokenType) error { @@ -1104,3 +1098,41 @@ func expiresAt(session fosite.Session, tokenType fosite.TokenType) *datatype.Dat func (s *Store) dbFor(ctx context.Context) *gorm.DB { return dbFromContext(ctx, s.db) } + +// Fosite reaches every storage through a provider accessor, and Store implements all of them itself + +func (s *Store) FositeClientManager() fosite.ClientManager { + return s +} + +func (s *Store) AuthorizeCodeStorage() fositeoauth2.AuthorizeCodeStorage { + return s +} + +func (s *Store) AccessTokenStorage() fositeoauth2.AccessTokenStorage { + return s +} + +func (s *Store) RefreshTokenStorage() fositeoauth2.RefreshTokenStorage { + return s +} + +func (s *Store) TokenRevocationStorage() fositeoauth2.TokenRevocationStorage { + return s +} + +func (s *Store) OpenIDConnectRequestStorage() openid.OpenIDConnectRequestStorage { + return s +} + +func (s *Store) PKCERequestStorage() pkce.PKCERequestStorage { + return s +} + +func (s *Store) DeviceAuthStorage() rfc8628.DeviceAuthStorage { + return s +} + +func (s *Store) PARStorage() fosite.PARStorage { + return s +} diff --git a/backend/internal/oidc/store_test.go b/backend/internal/oidc/store_test.go index 18352041..d18688a9 100644 --- a/backend/internal/oidc/store_test.go +++ b/backend/internal/oidc/store_test.go @@ -5,8 +5,8 @@ import ( "testing" "time" - "github.com/ory/fosite" - fositejwt "github.com/ory/fosite/token/jwt" + "github.com/pocket-id/fosite" + fositejwt "github.com/pocket-id/fosite/token/jwt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" @@ -69,8 +69,9 @@ func TestStoreInvalidateAuthorizeCodeSessionIsAtomic(t *testing.T) { store := NewStore(db, nil) const ( - clientID = "code-client" - code = "auth-code-single-use" + clientID = "code-client" + code = "auth-code-single-use" + signature = "auth-code-single-use-signature" ) require.NoError(t, db.Create(&model.OidcClient{Base: model.Base{ID: clientID}, Name: "Code Client"}).Error) @@ -84,17 +85,17 @@ func TestStoreInvalidateAuthorizeCodeSessionIsAtomic(t *testing.T) { Form: url.Values{}, Session: session, } - require.NoError(t, store.CreateAuthorizeCodeSession(t.Context(), code, request)) + require.NoError(t, store.CreateAuthorizeCodeSession(t.Context(), signature, request)) // First invalidation wins. - require.NoError(t, store.InvalidateAuthorizeCodeSession(t.Context(), code)) + require.NoError(t, store.InvalidateAuthorizeCodeSession(t.Context(), code, signature)) // A second invalidation of the now-inactive code fails closed: a racing token request - // cannot proceed to issue a second set of tokens from the same code. - require.ErrorIs(t, store.InvalidateAuthorizeCodeSession(t.Context(), code), fosite.ErrNotFound) + // cannot proceed to issue a second set of tokens from the same code, and the client gets invalid_grant. + require.ErrorIs(t, store.InvalidateAuthorizeCodeSession(t.Context(), code, signature), fosite.ErrInvalidGrant) // Reads of the consumed code report it as invalidated so fosite triggers reuse handling. - _, err := store.GetAuthorizeCodeSession(t.Context(), code, nil) + _, err := store.GetAuthorizeCodeSession(t.Context(), code, signature, nil) require.ErrorIs(t, err, fosite.ErrInvalidatedAuthorizeCode) } diff --git a/backend/internal/oidc/strategy_provider.go b/backend/internal/oidc/strategy_provider.go new file mode 100644 index 00000000..d5f21fa3 --- /dev/null +++ b/backend/internal/oidc/strategy_provider.go @@ -0,0 +1,57 @@ +package oidc + +import ( + fositeoauth2 "github.com/pocket-id/fosite/handler/oauth2" + "github.com/pocket-id/fosite/handler/openid" + "github.com/pocket-id/fosite/handler/rfc8628" + "github.com/pocket-id/fosite/token/jwt" +) + +// strategyProvider hands every Fosite handler the token strategy it needs +type strategyProvider struct { + opaque fositeoauth2.CoreStrategy + accessToken fositeoauth2.AccessTokenStrategy + device *deviceStrategy + idToken openid.OpenIDConnectTokenStrategy + + jwt.Signer +} + +var ( + _ fositeoauth2.AuthorizeCodeStrategyProvider = (*strategyProvider)(nil) + _ fositeoauth2.AccessTokenStrategyProvider = (*strategyProvider)(nil) + _ fositeoauth2.RefreshTokenStrategyProvider = (*strategyProvider)(nil) + _ openid.OpenIDConnectTokenStrategyProvider = (*strategyProvider)(nil) + _ rfc8628.DeviceRateLimitStrategyProvider = (*strategyProvider)(nil) + _ rfc8628.DeviceCodeStrategyProvider = (*strategyProvider)(nil) + _ rfc8628.UserCodeStrategyProvider = (*strategyProvider)(nil) + _ jwt.Signer = (*strategyProvider)(nil) +) + +func (s *strategyProvider) AuthorizeCodeStrategy() fositeoauth2.AuthorizeCodeStrategy { + return s.opaque +} + +func (s *strategyProvider) AccessTokenStrategy() fositeoauth2.AccessTokenStrategy { + return s.accessToken +} + +func (s *strategyProvider) RefreshTokenStrategy() fositeoauth2.RefreshTokenStrategy { + return s.opaque +} + +func (s *strategyProvider) OpenIDConnectTokenStrategy() openid.OpenIDConnectTokenStrategy { + return s.idToken +} + +func (s *strategyProvider) DeviceRateLimitStrategy() rfc8628.DeviceRateLimitStrategy { + return s.device +} + +func (s *strategyProvider) DeviceCodeStrategy() rfc8628.DeviceCodeStrategy { + return s.device +} + +func (s *strategyProvider) UserCodeStrategy() rfc8628.UserCodeStrategy { + return s.device +} diff --git a/backend/internal/oidc/token_handler.go b/backend/internal/oidc/token_handler.go index c9a57dce..de4d297b 100644 --- a/backend/internal/oidc/token_handler.go +++ b/backend/internal/oidc/token_handler.go @@ -6,7 +6,7 @@ import ( "slices" "github.com/gin-gonic/gin" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" ) type tokenHandler struct { diff --git a/backend/internal/oidc/token_handler_test.go b/backend/internal/oidc/token_handler_test.go index 4ba0bd96..afdf96de 100644 --- a/backend/internal/oidc/token_handler_test.go +++ b/backend/internal/oidc/token_handler_test.go @@ -15,10 +15,10 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/ory/fosite" - "github.com/ory/fosite/compose" - fositeoauth2 "github.com/ory/fosite/handler/oauth2" - fositejwt "github.com/ory/fosite/token/jwt" + "github.com/pocket-id/fosite" + "github.com/pocket-id/fosite/compose" + fositeoauth2 "github.com/pocket-id/fosite/handler/oauth2" + fositejwt "github.com/pocket-id/fosite/token/jwt" "github.com/stretchr/testify/require" "gorm.io/gorm" diff --git a/backend/internal/oidc/userinfo_handler.go b/backend/internal/oidc/userinfo_handler.go index 23b80b08..89faf22a 100644 --- a/backend/internal/oidc/userinfo_handler.go +++ b/backend/internal/oidc/userinfo_handler.go @@ -6,7 +6,7 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "gorm.io/gorm" ) diff --git a/backend/internal/oidc/userinfo_handler_test.go b/backend/internal/oidc/userinfo_handler_test.go index d541d20f..51c036fd 100644 --- a/backend/internal/oidc/userinfo_handler_test.go +++ b/backend/internal/oidc/userinfo_handler_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "github.com/stretchr/testify/require" "github.com/pocket-id/pocket-id/backend/internal/model" diff --git a/backend/internal/outbound/clients.go b/backend/internal/outbound/clients.go index 3bf4f066..a7ed7c7d 100644 --- a/backend/internal/outbound/clients.go +++ b/backend/internal/outbound/clients.go @@ -6,7 +6,7 @@ import ( "net/url" "strings" - "github.com/ory/fosite" + "github.com/pocket-id/fosite" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "github.com/pocket-id/pocket-id/backend/internal/common" diff --git a/backend/internal/service/e2etest_service.go b/backend/internal/service/e2etest_service.go index f4c67b34..2425012a 100644 --- a/backend/internal/service/e2etest_service.go +++ b/backend/internal/service/e2etest_service.go @@ -20,9 +20,9 @@ import ( "github.com/lestrrat-go/jwx/v4/jwa" "github.com/lestrrat-go/jwx/v4/jwk" "github.com/lestrrat-go/jwx/v4/jwt" - "github.com/ory/fosite" - "github.com/ory/fosite/compose" - fositejwt "github.com/ory/fosite/token/jwt" + "github.com/pocket-id/fosite" + "github.com/pocket-id/fosite/compose" + fositejwt "github.com/pocket-id/fosite/token/jwt" "github.com/pocket-id/pocket-id/backend/internal/apikey" "github.com/pocket-id/pocket-id/backend/internal/appconfig" "gorm.io/gorm" @@ -905,20 +905,14 @@ func seededRefreshTokenFixture(userID string, clientID string, fixtureRefreshTok } func (s *TestService) SignAccessToken(ctx context.Context, userID, clientID string, expired bool) (string, error) { - globalSecret, err := oidc.DeriveGlobalSecret(common.EnvConfig.EncryptionKey) - if err != nil { - return "", err - } fositeConfig := &fosite.Config{ - GlobalSecret: globalSecret, AccessTokenLifespan: AccessTokenDuration, AccessTokenIssuer: common.EnvConfig.AppURL, } - coreStrategy := compose.NewOAuth2HMACStrategy(fositeConfig) keyGetter := func(context.Context) (interface{}, error) { return oidc.SigningKeyFromSigner(s.jwtService) } - strategy := oidc.NewAccessTokenStrategy(compose.NewOAuth2RFC9068JWTStrategy(keyGetter, coreStrategy, fositeConfig), common.EnvConfig.AppURL) + strategy := oidc.NewAccessTokenStrategy(compose.NewOAuth2RFC9068JWTStrategy(keyGetter, fositeConfig), common.EnvConfig.AppURL) expiresAt := time.Now().UTC().Add(AccessTokenDuration) if expired { diff --git a/tests/setup/docker-compose.yml b/tests/setup/docker-compose.yml index bdf714eb..df8912b4 100644 --- a/tests/setup/docker-compose.yml +++ b/tests/setup/docker-compose.yml @@ -24,6 +24,8 @@ services: ENCRYPTION_KEY: test-encryption-key FILE_BACKEND: ${FILE_BACKEND} OUTBOUND_ALLOWED_HOSTS_BACKCHANNEL_LOGOUT: private,loopback,host.docker.internal + OUTBOUND_ALLOWED_HOSTS_SCIM: private,loopback,host.docker.internal + OUTBOUND_ALLOWED_HOSTS_FEDERATED_JWKS: private,loopback,host.docker.internal volumes: - pocket-id-test-data:/app/data build: