From 725424e839cbc1f62047c75059400a257b43409a Mon Sep 17 00:00:00 2001 From: zhouxhere Date: Thu, 28 Nov 2024 18:07:31 +0800 Subject: [PATCH] =?UTF-8?q?feat=20oidc=E5=9F=BA=E7=A1=80=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 20 +++++ go.sum | 46 +++++++++++ oauth/client.go | 3 + oauth/provider/provider.go | 39 ++++++++++ oauth/request.go | 97 +++++++++++++++++------ oauth/storage/auth.go | 152 +++++++++++++++++++++++++++++++++++++ oauth/storage/op.go | 49 ++++++++++++ oauth/storage/storage.go | 20 +++++ oauth/token.go | 6 ++ 9 files changed, 407 insertions(+), 25 deletions(-) create mode 100644 oauth/client.go create mode 100644 oauth/provider/provider.go create mode 100644 oauth/storage/auth.go create mode 100644 oauth/storage/op.go create mode 100644 oauth/storage/storage.go create mode 100644 oauth/token.go diff --git a/go.mod b/go.mod index 0591901..25b7253 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,10 @@ require ( gorm.io/gorm v1.25.12 ) +require gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect + require ( + github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect github.com/bytedance/sonic v1.12.4 // indirect github.com/bytedance/sonic/loader v0.2.1 // indirect github.com/cloudwego/base64x v0.1.4 // indirect @@ -20,11 +23,18 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/gabriel-vasile/mimetype v1.4.6 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-chi/chi/v5 v5.1.0 // indirect + github.com/go-jose/go-jose v2.6.3+incompatible + github.com/go-jose/go-jose/v4 v4.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.23.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.6.0 + github.com/gorilla/securecookie v1.1.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -41,9 +51,13 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/muhlemmer/gu v0.3.1 // indirect + github.com/muhlemmer/httpforwarded v0.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/rs/cors v1.11.1 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect @@ -52,12 +66,18 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/zitadel/logging v0.6.1 // indirect github.com/zitadel/oidc/v3 v3.33.1 // indirect + github.com/zitadel/schema v1.3.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.12.0 // indirect golang.org/x/crypto v0.29.0 // indirect golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/net v0.31.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.9.0 // indirect golang.org/x/sys v0.27.0 // indirect golang.org/x/text v0.20.0 // indirect diff --git a/go.sum b/go.sum index 8609060..2927d53 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k= github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= @@ -22,6 +24,17 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-jose/go-jose v2.6.3+incompatible h1:eU70erXEHN0wZl7K7kBTRLel/hu4P09qqopkDaXiXso= +github.com/go-jose/go-jose v2.6.3+incompatible/go.mod h1:coBhWG9DQz8V/JlBMg3LkUGnarUaxjQlWQUUv9Cv7tw= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -39,6 +52,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +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/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -78,6 +95,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= +github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= +github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY= +github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -87,11 +108,15 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -122,8 +147,24 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/zitadel/logging v0.6.1 h1:Vyzk1rl9Kq9RCevcpX6ujUaTYFX43aa4LkvV1TvUk+Y= +github.com/zitadel/logging v0.6.1/go.mod h1:Y4CyAXHpl3Mig6JOszcV5Rqqsojj+3n7y2F591Mp/ow= github.com/zitadel/oidc/v3 v3.33.1 h1:e3w9PDV0Mh50/ZiJWtzyT0E4uxJ6RXll+hqVDnqGbTU= github.com/zitadel/oidc/v3 v3.33.1/go.mod h1:zkoZ1Oq6CweX3BaLrftLEGCs6YK6zDpjjVGZrP10AWU= +github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= +github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg= @@ -138,11 +179,14 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -163,6 +207,8 @@ google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/go-jose/go-jose.v2 v2.6.3 h1:nt80fvSDlhKWQgSWyHyy5CfmlQr+asih51R8PTWNKKs= +gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/oauth/client.go b/oauth/client.go new file mode 100644 index 0000000..0706f81 --- /dev/null +++ b/oauth/client.go @@ -0,0 +1,3 @@ +package oauth + +type Client struct{} diff --git a/oauth/provider/provider.go b/oauth/provider/provider.go new file mode 100644 index 0000000..893d414 --- /dev/null +++ b/oauth/provider/provider.go @@ -0,0 +1,39 @@ +package provider + +import ( + "crypto/sha256" + "time" + + "github.com/zitadel/oidc/v3/pkg/op" + "golang.org/x/text/language" +) + +type Provider struct { + config *op.Config +} + +func NewProvider() *Provider { + + config := &op.Config{ + CryptoKey: sha256.Sum256([]byte("syz")), + DefaultLogoutRedirectURI: "/logged-out", + CodeMethodS256: true, + AuthMethodPost: true, + AuthMethodPrivateKeyJWT: true, + GrantTypeRefreshToken: true, + RequestObjectSupported: true, + SupportedUILocales: []language.Tag{language.Chinese}, + SupportedClaims: op.DefaultSupportedClaims, + SupportedScopes: op.DefaultSupportedScopes, + DeviceAuthorization: op.DeviceAuthorizationConfig{ + Lifetime: 5 * time.Minute, + PollInterval: 5 * time.Second, + UserFormPath: "/device", + UserCode: op.UserCodeBase20, + }, + } + + return &Provider{ + config: config, + } +} diff --git a/oauth/request.go b/oauth/request.go index d700507..efeda89 100644 --- a/oauth/request.go +++ b/oauth/request.go @@ -9,21 +9,30 @@ import ( ) type AuthRequest struct { - ID uuid.UUID `gorm:"primary_key;type:char(36);default:(UUID());comment:id"` - ClientID string `gorm:"type:varchar(255);not null;comment:client_id"` - UserID string `gorm:"type:varchar(255);not null;comment:user_id"` - Scopes []string `gorm:"type:text;comment:scopes"` - CallbackURI string `gorm:"type:varchar(255);not null;comment:callback_uri"` - State string `gorm:"type:varchar(255);not null;comment:state"` - Acr string `gorm:"type:varchar(255);not null;comment:acr"` - Amr []string `gorm:"type:text;comment:amr"` - ResponseType oidc.ResponseType `gorm:"type:varchar(255);not null;comment:response_type"` - ResponseMode oidc.ResponseMode `gorm:"type:varchar(255);not null;comment:response_mode"` - done bool `gorm:"type:boolean;not null;default:false;comment:done"` - authTime time.Time `gorm:"type:timestamp;not null;comment:auth_time"` - CreatedAt time.Time `gorm:"type:timestamp;not null;comment:created_at"` - UpdatedAt time.Time `gorm:"type:timestamp;not null;comment:updated_at"` - DeletedAt gorm.DeletedAt `gorm:"type:timestamp;comment:deleted_at"` + ID uuid.UUID `gorm:"primary_key;type:char(36);default:(UUID());comment:ID"` + ClientID string `gorm:"type:varchar(255);not null;comment:客户端ID"` + UserID string `gorm:"type:varchar(255);not null;comment:用户ID"` + Scopes []string `gorm:"type:text;comment:范围"` + Audience []string `gorm:"type:text;comment:受众"` + CallbackURI string `gorm:"type:varchar(255);not null;comment:回调URI"` + State string `gorm:"type:varchar(255);not null;comment:状态"` + Acr string `gorm:"type:varchar(255);not null;comment:认证上下文类引用"` + Amr []string `gorm:"type:text;comment:认证方法引用"` + ResponseType oidc.ResponseType `gorm:"type:varchar(255);not null;comment:响应类型"` + ResponseMode oidc.ResponseMode `gorm:"type:varchar(255);not null;comment:响应模式"` + CodeChallenge *OIDCCodeChallenge `gorm:"type:varchar(255);not null;comment:pkce参数"` + Nonce string `gorm:"type:varchar(255);not null;comment:随机数"` + done bool `gorm:"type:boolean;not null;default:false;comment:完成"` + authTime time.Time `gorm:"type:timestamp;comment:认证时间"` + CreatedAt time.Time `gorm:"type:timestamp;not null;autoCreateTime;comment:创建时间"` + UpdatedAt time.Time `gorm:"type:timestamp;comment:更新时间"` + DeletedAt gorm.DeletedAt `gorm:"type:timestamp;comment:删除时间"` +} + +type AuthCode struct { + ID uuid.UUID `gorm:"primary_key;type:char(36);default:(UUID());comment:ID"` + Code string `gorm:"type:varchar(255);not null;comment:授权码"` + RequestId uuid.UUID `gorm:"type:char(36);not null;comment:请求ID"` } func (a *AuthRequest) GetID() string { @@ -39,7 +48,7 @@ func (a *AuthRequest) GetAMR() []string { } func (a *AuthRequest) GetAudience() []string { - panic("not implemented") // TODO: Implement + return a.Audience } func (a *AuthRequest) GetAuthTime() time.Time { @@ -51,37 +60,75 @@ func (a *AuthRequest) GetClientID() string { } func (a *AuthRequest) GetCodeChallenge() *oidc.CodeChallenge { - panic("not implemented") // TODO: Implement + return CodeChallengeToOIDC(a.CodeChallenge) } func (a *AuthRequest) GetNonce() string { - panic("not implemented") // TODO: Implement + return a.Nonce } func (a *AuthRequest) GetRedirectURI() string { - panic("not implemented") // TODO: Implement + return a.CallbackURI } func (a *AuthRequest) GetResponseType() oidc.ResponseType { - panic("not implemented") // TODO: Implement + return a.ResponseType } func (a *AuthRequest) GetResponseMode() oidc.ResponseMode { - panic("not implemented") // TODO: Implement + return a.ResponseMode } func (a *AuthRequest) GetScopes() []string { - panic("not implemented") // TODO: Implement + return a.Scopes } func (a *AuthRequest) GetState() string { - panic("not implemented") // TODO: Implement + return a.State } func (a *AuthRequest) GetSubject() string { - panic("not implemented") // TODO: Implement + return a.UserID } func (a *AuthRequest) Done() bool { - panic("not implemented") // TODO: Implement + return a.done +} + +func AuthRequestFromOIDC(authRequest *oidc.AuthRequest, userID string) *AuthRequest { + return &AuthRequest{ + ClientID: authRequest.ClientID, + UserID: userID, + Scopes: authRequest.Scopes, + CallbackURI: authRequest.RedirectURI, + State: authRequest.State, + ResponseType: authRequest.ResponseType, + ResponseMode: authRequest.ResponseMode, + CodeChallenge: &OIDCCodeChallenge{ + Challenge: authRequest.CodeChallenge, + Method: string(authRequest.CodeChallengeMethod), + }, + Acr: authRequest.ACRValues.String(), + Nonce: authRequest.Nonce, + CreatedAt: time.Now(), + } +} + +type OIDCCodeChallenge struct { + Challenge string + Method string +} + +func CodeChallengeToOIDC(challenge *OIDCCodeChallenge) *oidc.CodeChallenge { + if challenge == nil { + return nil + } + challengeMethod := oidc.CodeChallengeMethodPlain + if challenge.Method == "S256" { + challengeMethod = oidc.CodeChallengeMethodS256 + } + return &oidc.CodeChallenge{ + Challenge: challenge.Challenge, + Method: challengeMethod, + } } diff --git a/oauth/storage/auth.go b/oauth/storage/auth.go new file mode 100644 index 0000000..ed346ce --- /dev/null +++ b/oauth/storage/auth.go @@ -0,0 +1,152 @@ +package storage + +import ( + "context" + "time" + + "git.zhouxhere.com/zhouxhere/syz/oauth" + "github.com/go-jose/go-jose" + "github.com/google/uuid" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" +) + +type AuthStorage struct { + op.AuthStorage +} + +func (s *Storage) CreateAuthRequest(ctx context.Context, authRequest *oidc.AuthRequest, userID string) (op.AuthRequest, error) { + s.lock.Lock() + defer s.lock.Unlock() + + request := oauth.AuthRequestFromOIDC(authRequest, userID) + + result := s.store.DB.Create(request) + + return request, result.Error +} + +func (s *Storage) AuthRequestByID(ctx context.Context, id string) (op.AuthRequest, error) { + s.lock.Lock() + defer s.lock.Unlock() + + var request oauth.AuthRequest + + result := s.store.DB.First(&request, id) + + return &request, result.Error +} + +func (s *Storage) AuthRequestByCode(ctx context.Context, code string) (op.AuthRequest, error) { + s.lock.Lock() + defer s.lock.Unlock() + + var authCode oauth.AuthCode + err := s.store.DB.Where("code = ?", code).First(&authCode).Error + if err != nil { + return nil, err + } + + var request oauth.AuthRequest + err = s.store.DB.Where("id = ?", authCode.RequestId).First(&request).Error + + return &request, err +} + +func (s *Storage) SaveAuthCode(ctx context.Context, requestID string, code string) error { + s.lock.Lock() + defer s.lock.Unlock() + + requestUUID, err := uuid.Parse(requestID) + + if err != nil { + return err + } + + authCode := oauth.AuthCode{ + RequestId: requestUUID, + Code: code, + } + + result := s.store.DB.Create(&authCode) + return result.Error +} + +func (s *Storage) DeleteAuthRequest(ctx context.Context, id string) error { + s.lock.Lock() + defer s.lock.Unlock() + + result := s.store.DB.Delete(&oauth.AuthCode{}, "request_id = ?", id) + + if result.Error != nil { + return result.Error + } + + result = s.store.DB.Delete(&oauth.AuthRequest{}, id) + return result.Error +} + +// The TokenRequest parameter of CreateAccessToken can be any of: +// +// * TokenRequest as returned by ClientCredentialsStorage.ClientCredentialsTokenRequest, +// +// * AuthRequest as returned by AuthRequestByID or AuthRequestByCode (above) +// +// - *oidc.JWTTokenRequest from a JWT that is the assertion value of a JWT Profile +// Grant: https://datatracker.ietf.org/doc/html/rfc7523#section-2.1 +// +// * TokenExchangeRequest as returned by ValidateTokenExchangeRequest +func (s *Storage) CreateAccessToken(ctx context.Context, tokenRequest op.TokenRequest) (accessTokenID string, expiration time.Time, err error) { + panic("not implemented") // TODO: Implement +} + +// The TokenRequest parameter of CreateAccessAndRefreshTokens can be any of: +// +// * TokenRequest as returned by ClientCredentialsStorage.ClientCredentialsTokenRequest +// +// * RefreshTokenRequest as returned by AuthStorage.TokenRequestByRefreshToken +// +// - AuthRequest as by returned by the AuthRequestByID or AuthRequestByCode (above). +// Used for the authorization code flow which requested offline_access scope and +// registered the refresh_token grant type in advance +// +// * TokenExchangeRequest as returned by ValidateTokenExchangeRequest +func (s *Storage) CreateAccessAndRefreshTokens(ctx context.Context, request op.TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshTokenID string, expiration time.Time, err error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) TokenRequestByRefreshToken(ctx context.Context, refreshTokenID string) (op.RefreshTokenRequest, error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) TerminateSession(ctx context.Context, userID string, clientID string) error { + panic("not implemented") // TODO: Implement +} + +// RevokeToken should revoke a token. In the situation that the original request was to +// revoke an access token, then tokenOrTokenID will be a tokenID and userID will be set +// but if the original request was for a refresh token, then userID will be empty and +// tokenOrTokenID will be the refresh token, not its ID. RevokeToken depends upon GetRefreshTokenInfo +// to get information from refresh tokens that are not either ":" strings +// nor JWTs. +func (s *Storage) RevokeToken(ctx context.Context, tokenOrTokenID string, userID string, clientID string) *oidc.Error { + panic("not implemented") // TODO: Implement +} + +// GetRefreshTokenInfo must return ErrInvalidRefreshToken when presented +// with a token that is not a refresh token. +func (s *Storage) GetRefreshTokenInfo(ctx context.Context, clientID string, token string) (userID string, tokenID string, err error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) SigningKey(_ context.Context) (op.SigningKey, error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) SignatureAlgorithms(_ context.Context) ([]jose.SignatureAlgorithm, error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) KeySet(_ context.Context) ([]op.Key, error) { + panic("not implemented") // TODO: Implement +} diff --git a/oauth/storage/op.go b/oauth/storage/op.go new file mode 100644 index 0000000..de4040d --- /dev/null +++ b/oauth/storage/op.go @@ -0,0 +1,49 @@ +package storage + +import ( + "context" + + "github.com/go-jose/go-jose/v4" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" +) + +type OPStorage struct { + op.OPStorage +} + +// GetClientByClientID loads a Client. The returned Client is never cached and is only used to +// handle the current request. +func (s *Storage) GetClientByClientID(ctx context.Context, clientID string) (op.Client, error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) AuthorizeClientIDSecret(ctx context.Context, clientID string, clientSecret string) error { + panic("not implemented") // TODO: Implement +} + +// SetUserinfoFromScopes is deprecated and should have an empty implementation for now. +// Implement SetUserinfoFromRequest instead. +func (s *Storage) SetUserinfoFromScopes(ctx context.Context, userinfo *oidc.UserInfo, userID string, clientID string, scopes []string) error { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) SetUserinfoFromToken(ctx context.Context, userinfo *oidc.UserInfo, tokenID string, subject string, origin string) error { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) SetIntrospectionFromToken(ctx context.Context, userinfo *oidc.IntrospectionResponse, tokenID string, subject string, clientID string) error { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) GetPrivateClaimsFromScopes(ctx context.Context, userID string, clientID string, scopes []string) (map[string]any, error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) GetKeyByIDAndClientID(ctx context.Context, keyID string, clientID string) (*jose.JSONWebKey, error) { + panic("not implemented") // TODO: Implement +} + +func (s *Storage) ValidateJWTProfileScopes(ctx context.Context, userID string, scopes []string) ([]string, error) { + panic("not implemented") // TODO: Implement +} diff --git a/oauth/storage/storage.go b/oauth/storage/storage.go new file mode 100644 index 0000000..494eb78 --- /dev/null +++ b/oauth/storage/storage.go @@ -0,0 +1,20 @@ +package storage + +import ( + "sync" + + "git.zhouxhere.com/zhouxhere/syz/store" +) + +type Storage struct { + lock sync.Mutex + store *store.Store + AuthStorage + OPStorage +} + +func NewStorage(store *store.Store) *Storage { + return &Storage{ + store: store, + } +} diff --git a/oauth/token.go b/oauth/token.go new file mode 100644 index 0000000..9ab33a2 --- /dev/null +++ b/oauth/token.go @@ -0,0 +1,6 @@ +package oauth + +type Token struct { +} + +type RefreshToken struct{}