diff --git a/.forgejo.bak/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml similarity index 99% rename from .forgejo.bak/ISSUE_TEMPLATE/bug_report.yaml rename to .github/ISSUE_TEMPLATE/bug_report.yaml index d024341..92465f9 100644 --- a/.forgejo.bak/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -2,7 +2,6 @@ name: Bug Report description: "Create a bug report to help us improve ZITADEL. Click [here](https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#product-management) to see how we process your issue." title: "[Bug]: " labels: ["bug"] -type: Bug body: - type: markdown attributes: diff --git a/.forgejo.bak/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml similarity index 100% rename from .forgejo.bak/ISSUE_TEMPLATE/config.yml rename to .github/ISSUE_TEMPLATE/config.yml diff --git a/.forgejo.bak/ISSUE_TEMPLATE/docs.yaml b/.github/ISSUE_TEMPLATE/docs.yaml similarity index 98% rename from .forgejo.bak/ISSUE_TEMPLATE/docs.yaml rename to .github/ISSUE_TEMPLATE/docs.yaml index d3f82b9..04c1c0c 100644 --- a/.forgejo.bak/ISSUE_TEMPLATE/docs.yaml +++ b/.github/ISSUE_TEMPLATE/docs.yaml @@ -1,7 +1,6 @@ name: 📄 Documentation description: Create an issue for missing or wrong documentation. labels: ["docs"] -type: task body: - type: markdown attributes: diff --git a/.forgejo.bak/ISSUE_TEMPLATE/enhancement.yaml b/.github/ISSUE_TEMPLATE/improvement.yaml similarity index 92% rename from .forgejo.bak/ISSUE_TEMPLATE/enhancement.yaml rename to .github/ISSUE_TEMPLATE/improvement.yaml index ef2103e..2e2ddf4 100644 --- a/.forgejo.bak/ISSUE_TEMPLATE/enhancement.yaml +++ b/.github/ISSUE_TEMPLATE/improvement.yaml @@ -1,12 +1,11 @@ name: 🛠️ Improvement description: "Create an new issue for an improvment in ZITADEL" -labels: ["enhancement"] -type: enhancement +labels: ["improvement"] body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this proposal / feature reqeust + Thanks for taking the time to fill out this improvement request - type: checkboxes id: preflight attributes: diff --git a/.github/ISSUE_TEMPLATE/proposal.yaml b/.github/ISSUE_TEMPLATE/proposal.yaml new file mode 100644 index 0000000..af7acd5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/proposal.yaml @@ -0,0 +1,44 @@ +name: 💡 Proposal / Feature request +description: "Create an issue for a feature request/proposal." +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this proposal / feature reqeust + - type: checkboxes + id: preflight + attributes: + label: Preflight Checklist + options: + - label: + I could not find a solution in the existing issues, docs, nor discussions + required: true + - label: + I have joined the [ZITADEL chat](https://zitadel.com/chat) + - type: textarea + id: problem + attributes: + label: Describe your problem + description: Please describe your problem this proposal / feature is supposed to solve. + placeholder: Describe the problem you have. + validations: + required: true + - type: textarea + id: solution + attributes: + label: Describe your ideal solution + description: Which solution do you propose? + placeholder: As a [type of user], I want [some goal] so that [some reason]. + validations: + required: true + - type: input + id: version + attributes: + label: Version + description: Which version of the OIDC Library are you using. + - type: textarea + id: additional + attributes: + label: Additional Context + description: Please add any other infos that could be useful. diff --git a/.forgejo.bak/dependabot.yml b/.github/dependabot.yml similarity index 100% rename from .forgejo.bak/dependabot.yml rename to .github/dependabot.yml diff --git a/.forgejo.bak/pull_request_template.md b/.github/pull_request_template.md similarity index 100% rename from .forgejo.bak/pull_request_template.md rename to .github/pull_request_template.md diff --git a/.forgejo.bak/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml similarity index 100% rename from .forgejo.bak/workflows/codeql-analysis.yml rename to .github/workflows/codeql-analysis.yml diff --git a/.forgejo.bak/workflows/issue.yml b/.github/workflows/issue.yml similarity index 100% rename from .forgejo.bak/workflows/issue.yml rename to .github/workflows/issue.yml diff --git a/.forgejo.bak/workflows/release.yml b/.github/workflows/release.yml similarity index 89% rename from .forgejo.bak/workflows/release.yml rename to .github/workflows/release.yml index 00063e4..48690cf 100644 --- a/.forgejo.bak/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,11 +14,11 @@ on: jobs: test: - runs-on: ubuntu-24.04 + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: - go: ['1.23', '1.24'] + go: ['1.21', '1.22'] name: Go ${{ matrix.go }} test steps: - uses: actions/checkout@v4 @@ -27,12 +27,12 @@ jobs: with: go-version: ${{ matrix.go }} - run: go test -race -v -coverprofile=profile.cov -coverpkg=./pkg/... ./pkg/... - - uses: codecov/codecov-action@v5.4.3 + - uses: codecov/codecov-action@v4.5.0 with: file: ./profile.cov name: codecov-go release: - runs-on: ubuntu-24.04 + runs-on: ubuntu-20.04 needs: [test] if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next' }} env: diff --git a/README.md b/README.md index bc346f5..01d7d47 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,9 @@ Whenever possible we tried to reuse / extend existing packages like `OAuth2 for ## Basic Overview The most important packages of the library: -
 /pkg
-    /client            clients using the OP for retrieving, exchanging and verifying tokens
+    /client            clients using the OP for retrieving, exchanging and verifying tokens       
         /rp            definition and implementation of an OIDC Relying Party (client)
         /rs            definition and implementation of an OAuth Resource Server (API)
     /op                definition and implementation of an OIDC OpenID Provider (server)
@@ -38,6 +37,7 @@ The most important packages of the library:
     /server            examples of an OpenID Provider implementations (including dynamic) with some very basic login UI
 
+ ### Semver This package uses [semver](https://semver.org/) for [releases](https://github.com/zitadel/oidc/releases). Major releases ship breaking changes. Starting with the `v2` to `v3` increment we provide an [upgrade guide](UPGRADING.md) to ease migration to a newer version. @@ -55,84 +55,48 @@ CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://localhost:9998/ SCOPES="openid ``` - open http://localhost:9999/login in your browser -- you will be redirected to op server and the login UI +- you will be redirected to op server and the login UI - login with user `test-user@localhost` and password `verysecure` - the OP will redirect you to the client app, which displays the user info for the dynamic issuer, just start it with: - ```bash go run github.com/zitadel/oidc/v3/example/server/dynamic -``` - +``` the oidc web client above will still work, but if you add `oidc.local` (pointing to 127.0.0.1) in your hosts file you can also start it with: - ```bash CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://oidc.local:9998/ SCOPES="openid profile" PORT=9999 go run github.com/zitadel/oidc/v3/example/client/app ``` > Note: Usernames are suffixed with the hostname (`test-user@localhost` or `test-user@oidc.local`) -### Server configuration - -Example server allows extra configuration using environment variables and could be used for end to -end testing of your services. - -| Name | Format | Description | -| ------------ | -------------------------------- | ------------------------------------- | -| PORT | Number between 1 and 65535 | OIDC listen port | -| REDIRECT_URI | Comma-separated URIs | List of allowed redirect URIs | -| USERS_FILE | Path to json in local filesystem | Users with their data and credentials | - -Here is json equivalent for one of the default users - -```json -{ - "id2": { - "ID": "id2", - "Username": "test-user2", - "Password": "verysecure", - "FirstName": "Test", - "LastName": "User2", - "Email": "test-user2@zitadel.ch", - "EmailVerified": true, - "Phone": "", - "PhoneVerified": false, - "PreferredLanguage": "DE", - "IsAdmin": false - } -} -``` - ## Features -| | Relying party | OpenID Provider | Specification | -| -------------------- | ------------- | --------------- | -------------------------------------------- | -| Code Flow | yes | yes | OpenID Connect Core 1.0, [Section 3.1][1] | -| Implicit Flow | no[^1] | yes | OpenID Connect Core 1.0, [Section 3.2][2] | -| Hybrid Flow | no | not yet | OpenID Connect Core 1.0, [Section 3.3][3] | -| Client Credentials | yes | yes | OpenID Connect Core 1.0, [Section 9][4] | -| Refresh Token | yes | yes | OpenID Connect Core 1.0, [Section 12][5] | -| Discovery | yes | yes | OpenID Connect [Discovery][6] 1.0 | -| JWT Profile | yes | yes | [RFC 7523][7] | -| PKCE | yes | yes | [RFC 7636][8] | -| Token Exchange | yes | yes | [RFC 8693][9] | -| Device Authorization | yes | yes | [RFC 8628][10] | -| mTLS | not yet | not yet | [RFC 8705][11] | -| Back-Channel Logout | not yet | yes | OpenID Connect [Back-Channel Logout][12] 1.0 | +| | Relying party | OpenID Provider | Specification | +| -------------------- | ------------- | --------------- | ----------------------------------------- | +| Code Flow | yes | yes | OpenID Connect Core 1.0, [Section 3.1][1] | +| Implicit Flow | no[^1] | yes | OpenID Connect Core 1.0, [Section 3.2][2] | +| Hybrid Flow | no | not yet | OpenID Connect Core 1.0, [Section 3.3][3] | +| Client Credentials | yes | yes | OpenID Connect Core 1.0, [Section 9][4] | +| Refresh Token | yes | yes | OpenID Connect Core 1.0, [Section 12][5] | +| Discovery | yes | yes | OpenID Connect [Discovery][6] 1.0 | +| JWT Profile | yes | yes | [RFC 7523][7] | +| PKCE | yes | yes | [RFC 7636][8] | +| Token Exchange | yes | yes | [RFC 8693][9] | +| Device Authorization | yes | yes | [RFC 8628][10] | +| mTLS | not yet | not yet | [RFC 8705][11] | -[1]: https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth "3.1. Authentication using the Authorization Code Flow" -[2]: https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth "3.2. Authentication using the Implicit Flow" -[3]: https://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth "3.3. Authentication using the Hybrid Flow" -[4]: https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication "9. Client Authentication" -[5]: https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens "12. Using Refresh Tokens" -[6]: https://openid.net/specs/openid-connect-discovery-1_0.html "OpenID Connect Discovery 1.0 incorporating errata set 1" -[7]: https://www.rfc-editor.org/rfc/rfc7523.html "JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants" -[8]: https://www.rfc-editor.org/rfc/rfc7636.html "Proof Key for Code Exchange by OAuth Public Clients" -[9]: https://www.rfc-editor.org/rfc/rfc8693.html "OAuth 2.0 Token Exchange" -[10]: https://www.rfc-editor.org/rfc/rfc8628.html "OAuth 2.0 Device Authorization Grant" -[11]: https://www.rfc-editor.org/rfc/rfc8705.html "OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens" -[12]: https://openid.net/specs/openid-connect-backchannel-1_0.html "OpenID Connect Back-Channel Logout 1.0 incorporating errata set 1" +[1]: "3.1. Authentication using the Authorization Code Flow" +[2]: "3.2. Authentication using the Implicit Flow" +[3]: "3.3. Authentication using the Hybrid Flow" +[4]: "9. Client Authentication" +[5]: "12. Using Refresh Tokens" +[6]: "OpenID Connect Discovery 1.0 incorporating errata set 1" +[7]: "JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants" +[8]: "Proof Key for Code Exchange by OAuth Public Clients" +[9]: "OAuth 2.0 Token Exchange" +[10]: "OAuth 2.0 Device Authorization Grant" +[11]: "OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens" ## Contributors @@ -151,14 +115,14 @@ For your convenience you can find the relevant guides linked below. ## Supported Go Versions -For security reasons, we only support and recommend the use of one of the latest two Go versions (:white_check_mark:). +For security reasons, we only support and recommend the use of one of the latest two Go versions (:white_check_mark:). Versions that also build are marked with :warning:. | Version | Supported | | ------- | ------------------ | -| <1.23 | :x: | -| 1.23 | :white_check_mark: | -| 1.24 | :white_check_mark: | +| <1.21 | :x: | +| 1.21 | :white_check_mark: | +| 1.22 | :white_check_mark: | ## Why another library @@ -189,4 +153,5 @@ Unless required by applicable law or agreed to in writing, software distributed AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + [^1]: https://github.com/zitadel/oidc/issues/135#issuecomment-950563892 diff --git a/example/client/api/api.go b/example/client/api/api.go index 69f9466..2e61c21 100644 --- a/example/client/api/api.go +++ b/example/client/api/api.go @@ -13,8 +13,8 @@ import ( "github.com/go-chi/chi/v5" "github.com/sirupsen/logrus" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rs" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rs" + "github.com/zitadel/oidc/v3/pkg/oidc" ) const ( diff --git a/example/client/app/app.go b/example/client/app/app.go index 90b1969..a870e00 100644 --- a/example/client/app/app.go +++ b/example/client/app/app.go @@ -11,13 +11,14 @@ import ( "sync/atomic" "time" + "github.com/go-jose/go-jose/v4" "github.com/google/uuid" "github.com/sirupsen/logrus" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/zitadel/logging" + "github.com/zitadel/oidc/v3/pkg/client/rp" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) var ( @@ -53,10 +54,25 @@ func main() { options := []rp.Option{ rp.WithCookieHandler(cookieHandler), - rp.WithVerifierOpts(rp.WithIssuedAtOffset(5 * time.Second)), + rp.WithVerifierOpts( + rp.WithIssuedAtOffset(5*time.Second), + // When the OP uses other signing algorithms then RS256, + // We need to tell the RP to accept them. + // The actual handshake is done with the "kid" and "alg" header claims. + // However, [jose.ParseSigned] needs a list of algorithms we are willing to accept. + // This example sets all the algorithms the ZITADEL product supports. + rp.WithSupportedSigningAlgorithms( + string(jose.EdDSA), + string(jose.RS256), + string(jose.RS384), + string(jose.RS512), + string(jose.ES256), + string(jose.ES384), + string(jose.ES512), + ), + ), rp.WithHTTPClient(client), rp.WithLogger(logger), - rp.WithSigningAlgsFromDiscovery(), } if clientSecret == "" { options = append(options, rp.WithPKCE(cookieHandler)) diff --git a/example/client/device/device.go b/example/client/device/device.go index 33bc570..78ed2c8 100644 --- a/example/client/device/device.go +++ b/example/client/device/device.go @@ -45,8 +45,8 @@ import ( "github.com/sirupsen/logrus" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/client/rp" + httphelper "github.com/zitadel/oidc/v3/pkg/http" ) var ( diff --git a/example/client/github/github.go b/example/client/github/github.go index f6c536b..7d069d4 100644 --- a/example/client/github/github.go +++ b/example/client/github/github.go @@ -10,10 +10,10 @@ import ( "golang.org/x/oauth2" githubOAuth "golang.org/x/oauth2/github" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp/cli" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rp/cli" + "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) var ( diff --git a/example/client/service/service.go b/example/client/service/service.go index a88ab2f..865a4e0 100644 --- a/example/client/service/service.go +++ b/example/client/service/service.go @@ -13,7 +13,7 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/oauth2" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/profile" + "github.com/zitadel/oidc/v3/pkg/client/profile" ) var client = http.DefaultClient diff --git a/example/server/config/config.go b/example/server/config/config.go deleted file mode 100644 index 96837d4..0000000 --- a/example/server/config/config.go +++ /dev/null @@ -1,40 +0,0 @@ -package config - -import ( - "os" - "strings" -) - -const ( - // default port for the http server to run - DefaultIssuerPort = "9998" -) - -type Config struct { - Port string - RedirectURI []string - UsersFile string -} - -// FromEnvVars loads configuration parameters from environment variables. -// If there is no such variable defined, then use default values. -func FromEnvVars(defaults *Config) *Config { - if defaults == nil { - defaults = &Config{} - } - cfg := &Config{ - Port: defaults.Port, - RedirectURI: defaults.RedirectURI, - UsersFile: defaults.UsersFile, - } - if value, ok := os.LookupEnv("PORT"); ok { - cfg.Port = value - } - if value, ok := os.LookupEnv("USERS_FILE"); ok { - cfg.UsersFile = value - } - if value, ok := os.LookupEnv("REDIRECT_URI"); ok { - cfg.RedirectURI = strings.Split(value, ",") - } - return cfg -} diff --git a/example/server/config/config_test.go b/example/server/config/config_test.go deleted file mode 100644 index 3b73c0b..0000000 --- a/example/server/config/config_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package config - -import ( - "fmt" - "os" - "testing" -) - -func TestFromEnvVars(t *testing.T) { - - for _, tc := range []struct { - name string - env map[string]string - defaults *Config - want *Config - }{ - { - name: "no vars, no default values", - env: map[string]string{}, - want: &Config{}, - }, - { - name: "no vars, only defaults", - env: map[string]string{}, - defaults: &Config{ - Port: "6666", - UsersFile: "/default/user/path", - RedirectURI: []string{"re", "direct", "uris"}, - }, - want: &Config{ - Port: "6666", - UsersFile: "/default/user/path", - RedirectURI: []string{"re", "direct", "uris"}, - }, - }, - { - name: "overriding default values", - env: map[string]string{ - "PORT": "1234", - "USERS_FILE": "/path/to/users", - "REDIRECT_URI": "http://redirect/redirect", - }, - defaults: &Config{ - Port: "6666", - UsersFile: "/default/user/path", - RedirectURI: []string{"re", "direct", "uris"}, - }, - want: &Config{ - Port: "1234", - UsersFile: "/path/to/users", - RedirectURI: []string{"http://redirect/redirect"}, - }, - }, - { - name: "multiple redirect uris", - env: map[string]string{ - "REDIRECT_URI": "http://host_1,http://host_2,http://host_3", - }, - want: &Config{ - RedirectURI: []string{ - "http://host_1", "http://host_2", "http://host_3", - }, - }, - }, - } { - t.Run(tc.name, func(t *testing.T) { - os.Clearenv() - for k, v := range tc.env { - os.Setenv(k, v) - } - cfg := FromEnvVars(tc.defaults) - if fmt.Sprint(cfg) != fmt.Sprint(tc.want) { - t.Errorf("Expected FromEnvVars()=%q, but got %q", tc.want, cfg) - } - }) - } -} diff --git a/example/server/dynamic/login.go b/example/server/dynamic/login.go index 05f0e34..685b444 100644 --- a/example/server/dynamic/login.go +++ b/example/server/dynamic/login.go @@ -8,7 +8,7 @@ import ( "github.com/go-chi/chi/v5" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op" ) const ( diff --git a/example/server/dynamic/op.go b/example/server/dynamic/op.go index 2c00e41..432a575 100644 --- a/example/server/dynamic/op.go +++ b/example/server/dynamic/op.go @@ -10,8 +10,8 @@ import ( "github.com/go-chi/chi/v5" "golang.org/x/text/language" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/storage" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/example/server/storage" + "github.com/zitadel/oidc/v3/pkg/op" ) const ( diff --git a/example/server/exampleop/device.go b/example/server/exampleop/device.go index 99505e4..2f9be52 100644 --- a/example/server/exampleop/device.go +++ b/example/server/exampleop/device.go @@ -8,10 +8,10 @@ import ( "net/http" "net/url" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" "github.com/go-chi/chi/v5" "github.com/gorilla/securecookie" "github.com/sirupsen/logrus" + "github.com/zitadel/oidc/v3/pkg/op" ) type deviceAuthenticate interface { diff --git a/example/server/exampleop/login.go b/example/server/exampleop/login.go index 77a6189..4d2b478 100644 --- a/example/server/exampleop/login.go +++ b/example/server/exampleop/login.go @@ -5,8 +5,8 @@ import ( "fmt" "net/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" "github.com/go-chi/chi/v5" + "github.com/zitadel/oidc/v3/pkg/op" ) type login struct { diff --git a/example/server/exampleop/op.go b/example/server/exampleop/op.go index e12c755..e8ef892 100644 --- a/example/server/exampleop/op.go +++ b/example/server/exampleop/op.go @@ -12,13 +12,22 @@ import ( "github.com/zitadel/logging" "golang.org/x/text/language" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/example/server/storage" + "github.com/zitadel/oidc/v3/pkg/op" ) const ( pathLoggedOut = "/logged-out" ) +func init() { + storage.RegisterClients( + storage.NativeClient("native"), + storage.WebClient("web", "secret"), + storage.WebClient("api", "secret"), + ) +} + type Storage interface { op.Storage authenticate diff --git a/example/server/main.go b/example/server/main.go index 5bdbb05..a2ad190 100644 --- a/example/server/main.go +++ b/example/server/main.go @@ -6,53 +6,36 @@ import ( "net/http" "os" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/config" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/exampleop" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/storage" + "github.com/zitadel/oidc/v3/example/server/exampleop" + "github.com/zitadel/oidc/v3/example/server/storage" ) -func getUserStore(cfg *config.Config) (storage.UserStore, error) { - if cfg.UsersFile == "" { - return storage.NewUserStore(fmt.Sprintf("http://localhost:%s/", cfg.Port)), nil - } - return storage.StoreFromFile(cfg.UsersFile) -} - func main() { - cfg := config.FromEnvVars(&config.Config{Port: "9998"}) + //we will run on :9998 + port := "9998" + //which gives us the issuer: http://localhost:9998/ + issuer := fmt.Sprintf("http://localhost:%s/", port) + + // the OpenIDProvider interface needs a Storage interface handling various checks and state manipulations + // this might be the layer for accessing your database + // in this example it will be handled in-memory + storage := storage.NewStorage(storage.NewUserStore(issuer)) + logger := slog.New( slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelDebug, }), ) - - //which gives us the issuer: http://localhost:9998/ - issuer := fmt.Sprintf("http://localhost:%s/", cfg.Port) - - storage.RegisterClients( - storage.NativeClient("native", cfg.RedirectURI...), - storage.WebClient("web", "secret", cfg.RedirectURI...), - storage.WebClient("api", "secret", cfg.RedirectURI...), - ) - - // the OpenIDProvider interface needs a Storage interface handling various checks and state manipulations - // this might be the layer for accessing your database - // in this example it will be handled in-memory - store, err := getUserStore(cfg) - if err != nil { - logger.Error("cannot create UserStore", "error", err) - os.Exit(1) - } - storage := storage.NewStorage(store) router := exampleop.SetupServer(issuer, storage, logger, false) server := &http.Server{ - Addr: ":" + cfg.Port, + Addr: ":" + port, Handler: router, } - logger.Info("server listening, press ctrl+c to stop", "addr", issuer) - if server.ListenAndServe() != http.ErrServerClosed { + logger.Info("server listening, press ctrl+c to stop", "addr", fmt.Sprintf("http://localhost:%s/", port)) + err := server.ListenAndServe() + if err != http.ErrServerClosed { logger.Error("server terminated", "error", err) os.Exit(1) } diff --git a/example/server/storage/client.go b/example/server/storage/client.go index 2b836c0..010b9ce 100644 --- a/example/server/storage/client.go +++ b/example/server/storage/client.go @@ -3,8 +3,8 @@ package storage import ( "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) var ( diff --git a/example/server/storage/oidc.go b/example/server/storage/oidc.go index 9c7f544..9cd08d9 100644 --- a/example/server/storage/oidc.go +++ b/example/server/storage/oidc.go @@ -6,8 +6,8 @@ import ( "golang.org/x/text/language" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) const ( @@ -121,7 +121,7 @@ func (a *AuthRequest) Done() bool { } func PromptToInternal(oidcPrompt oidc.SpaceDelimitedArray) []string { - prompts := make([]string, 0, len(oidcPrompt)) + prompts := make([]string, len(oidcPrompt)) for _, oidcPrompt := range oidcPrompt { switch oidcPrompt { case oidc.PromptNone, @@ -164,15 +164,6 @@ func authRequestToInternal(authReq *oidc.AuthRequest, userID string) *AuthReques } } -type AuthRequestWithSessionState struct { - *AuthRequest - SessionState string -} - -func (a *AuthRequestWithSessionState) GetSessionState() string { - return a.SessionState -} - type OIDCCodeChallenge struct { Challenge string Method string diff --git a/example/server/storage/storage.go b/example/server/storage/storage.go index d4315c6..d8b7a5d 100644 --- a/example/server/storage/storage.go +++ b/example/server/storage/storage.go @@ -14,8 +14,8 @@ import ( jose "github.com/go-jose/go-jose/v4" "github.com/google/uuid" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) // serviceKey1 is a public key which will be used for the JWT Profile Authorization Grant @@ -151,9 +151,6 @@ func (s *Storage) CheckUsernamePassword(username, password, id string) error { // in this example we'll simply check the username / password and set a boolean to true // therefore we will also just check this boolean if the request / login has been finished request.done = true - - request.authTime = time.Now() - return nil } return fmt.Errorf("username or password wrong") @@ -298,19 +295,15 @@ func (s *Storage) CreateAccessAndRefreshTokens(ctx context.Context, request op.T // if we get here, the currentRefreshToken was not empty, so the call is a refresh token request // we therefore will have to check the currentRefreshToken and renew the refresh token - - newRefreshToken = uuid.NewString() - - accessToken, err := s.accessToken(applicationID, newRefreshToken, request.GetSubject(), request.GetAudience(), request.GetScopes()) + refreshToken, refreshTokenID, err := s.renewRefreshToken(currentRefreshToken) if err != nil { return "", "", time.Time{}, err } - - if err := s.renewRefreshToken(currentRefreshToken, newRefreshToken, accessToken.ID); err != nil { + accessToken, err := s.accessToken(applicationID, refreshTokenID, request.GetSubject(), request.GetAudience(), request.GetScopes()) + if err != nil { return "", "", time.Time{}, err } - - return accessToken.ID, newRefreshToken, accessToken.Expiration, nil + return accessToken.ID, refreshToken, accessToken.Expiration, nil } func (s *Storage) exchangeRefreshToken(ctx context.Context, request op.TokenExchangeRequest) (accessTokenID string, newRefreshToken string, expiration time.Time, err error) { @@ -392,9 +385,14 @@ func (s *Storage) RevokeToken(ctx context.Context, tokenIDOrToken string, userID if refreshToken.ApplicationID != clientID { return oidc.ErrInvalidClient().WithDescription("token was not issued for this client") } - delete(s.refreshTokens, refreshToken.ID) // if it is a refresh token, you will have to remove the access token as well - delete(s.tokens, refreshToken.AccessToken) + delete(s.refreshTokens, refreshToken.ID) + for _, accessToken := range s.tokens { + if accessToken.RefreshTokenID == refreshToken.ID { + delete(s.tokens, accessToken.ID) + return nil + } + } return nil } @@ -490,9 +488,6 @@ func (s *Storage) SetUserinfoFromToken(ctx context.Context, userinfo *oidc.UserI // return err // } //} - if token.Expiration.Before(time.Now()) { - return fmt.Errorf("token is expired") - } return s.setUserinfo(ctx, userinfo, token.Subject, token.ApplicationID, token.Scopes) } @@ -599,41 +594,33 @@ func (s *Storage) createRefreshToken(accessToken *Token, amr []string, authTime Audience: accessToken.Audience, Expiration: time.Now().Add(5 * time.Hour), Scopes: accessToken.Scopes, - AccessToken: accessToken.ID, } s.refreshTokens[token.ID] = token return token.Token, nil } // renewRefreshToken checks the provided refresh_token and creates a new one based on the current -// -// [Refresh Token Rotation] is implemented. -// -// [Refresh Token Rotation]: https://www.rfc-editor.org/rfc/rfc6819#section-5.2.2.3 -func (s *Storage) renewRefreshToken(currentRefreshToken, newRefreshToken, newAccessToken string) error { +func (s *Storage) renewRefreshToken(currentRefreshToken string) (string, string, error) { s.lock.Lock() defer s.lock.Unlock() refreshToken, ok := s.refreshTokens[currentRefreshToken] if !ok { - return fmt.Errorf("invalid refresh token") + return "", "", fmt.Errorf("invalid refresh token") } - // deletes the refresh token + // deletes the refresh token and all access tokens which were issued based on this refresh token delete(s.refreshTokens, currentRefreshToken) - - // delete the access token which was issued based on this refresh token - delete(s.tokens, refreshToken.AccessToken) - - if refreshToken.Expiration.Before(time.Now()) { - return fmt.Errorf("expired refresh token") + for _, token := range s.tokens { + if token.RefreshTokenID == currentRefreshToken { + delete(s.tokens, token.ID) + break + } } - // creates a new refresh token based on the current one - refreshToken.Token = newRefreshToken - refreshToken.ID = newRefreshToken - refreshToken.Expiration = time.Now().Add(5 * time.Hour) - refreshToken.AccessToken = newAccessToken - s.refreshTokens[newRefreshToken] = refreshToken - return nil + token := uuid.NewString() + refreshToken.Token = token + refreshToken.ID = token + s.refreshTokens[token] = refreshToken + return token, refreshToken.ID, nil } // accessToken will store an access_token in-memory based on the provided information diff --git a/example/server/storage/storage_dynamic.go b/example/server/storage/storage_dynamic.go index 765d29a..d112d71 100644 --- a/example/server/storage/storage_dynamic.go +++ b/example/server/storage/storage_dynamic.go @@ -6,8 +6,8 @@ import ( jose "github.com/go-jose/go-jose/v4" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) type multiStorage struct { diff --git a/example/server/storage/token.go b/example/server/storage/token.go index beab38c..ad907e3 100644 --- a/example/server/storage/token.go +++ b/example/server/storage/token.go @@ -22,5 +22,4 @@ type RefreshToken struct { ApplicationID string Expiration time.Time Scopes []string - AccessToken string // Token.ID } diff --git a/example/server/storage/user.go b/example/server/storage/user.go index ed8cdfa..173daef 100644 --- a/example/server/storage/user.go +++ b/example/server/storage/user.go @@ -2,8 +2,6 @@ package storage import ( "crypto/rsa" - "encoding/json" - "os" "strings" "golang.org/x/text/language" @@ -37,18 +35,6 @@ type userStore struct { users map[string]*User } -func StoreFromFile(path string) (UserStore, error) { - users := map[string]*User{} - data, err := os.ReadFile(path) - if err != nil { - return nil, err - } - if err := json.Unmarshal(data, &users); err != nil { - return nil, err - } - return userStore{users}, nil -} - func NewUserStore(issuer string) UserStore { hostname := strings.Split(strings.Split(issuer, "://")[1], ":")[0] return userStore{ diff --git a/example/server/storage/user_test.go b/example/server/storage/user_test.go deleted file mode 100644 index c2e2212..0000000 --- a/example/server/storage/user_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package storage - -import ( - "os" - "path" - "reflect" - "testing" - - "golang.org/x/text/language" -) - -func TestStoreFromFile(t *testing.T) { - for _, tc := range []struct { - name string - pathToFile string - content string - want UserStore - wantErr bool - }{ - { - name: "normal user file", - pathToFile: "userfile.json", - content: `{ - "id1": { - "ID": "id1", - "EmailVerified": true, - "PreferredLanguage": "DE" - } - }`, - want: userStore{map[string]*User{ - "id1": { - ID: "id1", - EmailVerified: true, - PreferredLanguage: language.German, - }, - }}, - }, - { - name: "malformed file", - pathToFile: "whatever", - content: "not a json just a text", - wantErr: true, - }, - { - name: "not existing file", - pathToFile: "what/ever/file", - wantErr: true, - }, - } { - t.Run(tc.name, func(t *testing.T) { - actualPath := path.Join(t.TempDir(), tc.pathToFile) - - if tc.content != "" && tc.pathToFile != "" { - if err := os.WriteFile(actualPath, []byte(tc.content), 0666); err != nil { - t.Fatalf("cannot create file with test content: %q", tc.content) - } - } - result, err := StoreFromFile(actualPath) - if err != nil && !tc.wantErr { - t.Errorf("StoreFromFile(%q) returned unexpected error %q", tc.pathToFile, err) - } else if err == nil && tc.wantErr { - t.Errorf("StoreFromFile(%q) did not return an expected error", tc.pathToFile) - } - if !tc.wantErr && !reflect.DeepEqual(tc.want, result.(userStore)) { - t.Errorf("expected StoreFromFile(%q) = %v, but got %v", - tc.pathToFile, tc.want, result) - } - }) - } -} diff --git a/go.mod b/go.mod index a0f42c4..6533530 100644 --- a/go.mod +++ b/go.mod @@ -1,28 +1,26 @@ -module git.christmann.info/LARA/zitadel-oidc/v3 +module github.com/zitadel/oidc/v3 -go 1.23.7 - -toolchain go1.24.1 +go 1.21 require ( - github.com/bmatcuk/doublestar/v4 v4.8.1 - github.com/go-chi/chi/v5 v5.2.1 - github.com/go-jose/go-jose/v4 v4.0.5 + github.com/bmatcuk/doublestar/v4 v4.6.1 + github.com/go-chi/chi/v5 v5.1.0 + github.com/go-jose/go-jose/v4 v4.0.4 github.com/golang/mock v1.6.0 github.com/google/go-github/v31 v31.0.0 github.com/google/uuid v1.6.0 github.com/gorilla/securecookie v1.1.2 - github.com/jeremija/gosubmit v0.2.8 + github.com/jeremija/gosubmit v0.2.7 github.com/muhlemmer/gu v0.3.1 github.com/muhlemmer/httpforwarded v0.1.0 - github.com/rs/cors v1.11.1 + github.com/rs/cors v1.11.0 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.10.0 - github.com/zitadel/logging v0.6.2 - github.com/zitadel/schema v1.3.1 - go.opentelemetry.io/otel v1.29.0 - golang.org/x/oauth2 v0.30.0 - golang.org/x/text v0.26.0 + github.com/stretchr/testify v1.9.0 + github.com/zitadel/logging v0.6.0 + github.com/zitadel/schema v1.3.0 + go.opentelemetry.io/otel v1.28.0 + golang.org/x/oauth2 v0.22.0 + golang.org/x/text v0.17.0 ) require ( @@ -31,10 +29,10 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.29.0 // indirect - golang.org/x/crypto v0.36.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/sys v0.31.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.22.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4835505..19c1724 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,12 @@ -github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38= -github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= -github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= -github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= +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/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= @@ -29,8 +29,8 @@ 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/jeremija/gosubmit v0.2.8 h1:mmSITBz9JxVtu8eqbN+zmmwX7Ij2RidQxhcwRVI4wqA= -github.com/jeremija/gosubmit v0.2.8/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= +github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc= +github.com/jeremija/gosubmit v0.2.7/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= @@ -41,40 +41,40 @@ github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/ github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= -github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/zitadel/logging v0.6.2 h1:MW2kDDR0ieQynPZ0KIZPrh9ote2WkxfBif5QoARDQcU= -github.com/zitadel/logging v0.6.2/go.mod h1:z6VWLWUkJpnNVDSLzrPSQSQyttysKZ6bCRongw0ROK4= -github.com/zitadel/schema v1.3.1 h1:QT3kwiRIRXXLVAs6gCK/u044WmUVh6IlbLXUsn6yRQU= -github.com/zitadel/schema v1.3.1/go.mod h1:071u7D2LQacy1HAN+YnMd/mx1qVE2isb0Mjeqg46xnU= -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/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/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +github.com/zitadel/logging v0.6.0 h1:t5Nnt//r+m2ZhhoTmoPX+c96pbMarqJvW1Vq6xFTank= +github.com/zitadel/logging v0.6.0/go.mod h1:Y4CyAXHpl3Mig6JOszcV5Rqqsojj+3n7y2F591Mp/ow= +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.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -83,13 +83,13 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -101,8 +101,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/testutil/gen/gen.go b/internal/testutil/gen/gen.go index 3e44b7d..e4a5718 100644 --- a/internal/testutil/gen/gen.go +++ b/internal/testutil/gen/gen.go @@ -8,8 +8,8 @@ import ( "fmt" "os" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" ) var custom = map[string]any{ diff --git a/internal/testutil/token.go b/internal/testutil/token.go index 72d08c5..7ad8893 100644 --- a/internal/testutil/token.go +++ b/internal/testutil/token.go @@ -8,9 +8,9 @@ import ( "errors" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" jose "github.com/go-jose/go-jose/v4" "github.com/muhlemmer/gu" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // KeySet implements oidc.Keys diff --git a/pkg/client/client.go b/pkg/client/client.go index 2e1f536..990da9b 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -15,9 +15,9 @@ import ( "go.opentelemetry.io/otel" "golang.org/x/oauth2" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/crypto" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/crypto" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) var ( @@ -42,7 +42,7 @@ func Discover(ctx context.Context, issuer string, httpClient *http.Client, wellK discoveryConfig := new(oidc.DiscoveryConfiguration) err = httphelper.HttpRequest(httpClient, req, &discoveryConfig) if err != nil { - return nil, errors.Join(oidc.ErrDiscoveryFailed, err) + return nil, err } if logger, ok := logging.FromContext(ctx); ok { logger.Debug("discover", "config", discoveryConfig) diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index 9e21e8e..e06c825 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -5,7 +5,6 @@ import ( "net/http" "testing" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -23,7 +22,7 @@ func TestDiscover(t *testing.T) { name string args args wantFields *wantFields - wantErr error + wantErr bool }{ { name: "spotify", // https://github.com/zitadel/oidc/issues/406 @@ -33,20 +32,17 @@ func TestDiscover(t *testing.T) { wantFields: &wantFields{ UILocalesSupported: true, }, - wantErr: nil, - }, - { - name: "discovery failed", - args: args{ - issuer: "https://example.com", - }, - wantErr: oidc.ErrDiscoveryFailed, + wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := Discover(context.Background(), tt.args.issuer, http.DefaultClient, tt.args.wellKnownUrl...) - require.ErrorIs(t, err, tt.wantErr) + if tt.wantErr { + assert.Error(t, err) + return + } + require.NoError(t, err) if tt.wantFields == nil { return } diff --git a/pkg/client/integration_test.go b/pkg/client/integration_test.go index 86a9ab7..98a9d3a 100644 --- a/pkg/client/integration_test.go +++ b/pkg/client/integration_test.go @@ -23,14 +23,14 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/oauth2" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/exampleop" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/storage" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rs" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/tokenexchange" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/example/server/exampleop" + "github.com/zitadel/oidc/v3/example/server/storage" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/client/rs" + "github.com/zitadel/oidc/v3/pkg/client/tokenexchange" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) var Logger = slog.New( diff --git a/pkg/client/jwt_profile.go b/pkg/client/jwt_profile.go index 98a54fd..0a5d9ec 100644 --- a/pkg/client/jwt_profile.go +++ b/pkg/client/jwt_profile.go @@ -6,8 +6,8 @@ import ( "golang.org/x/oauth2" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // JWTProfileExchange handles the oauth2 jwt profile exchange diff --git a/pkg/client/key.go b/pkg/client/key.go index 7f38311..0c01dd2 100644 --- a/pkg/client/key.go +++ b/pkg/client/key.go @@ -2,7 +2,7 @@ package client import ( "encoding/json" - "os" + "io/ioutil" ) const ( @@ -24,7 +24,7 @@ type KeyFile struct { } func ConfigFromKeyFile(path string) (*KeyFile, error) { - data, err := os.ReadFile(path) + data, err := ioutil.ReadFile(path) if err != nil { return nil, err } diff --git a/pkg/client/profile/jwt_profile.go b/pkg/client/profile/jwt_profile.go index fb351f0..060f390 100644 --- a/pkg/client/profile/jwt_profile.go +++ b/pkg/client/profile/jwt_profile.go @@ -8,8 +8,8 @@ import ( jose "github.com/go-jose/go-jose/v4" "golang.org/x/oauth2" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type TokenSource interface { diff --git a/pkg/client/rp/cli/cli.go b/pkg/client/rp/cli/cli.go index 10edaa7..eeb9011 100644 --- a/pkg/client/rp/cli/cli.go +++ b/pkg/client/rp/cli/cli.go @@ -4,9 +4,9 @@ import ( "context" "net/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) const ( diff --git a/pkg/client/rp/delegation.go b/pkg/client/rp/delegation.go index fb4fc63..23ecffd 100644 --- a/pkg/client/rp/delegation.go +++ b/pkg/client/rp/delegation.go @@ -1,7 +1,7 @@ package rp import ( - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc/grants/tokenexchange" + "github.com/zitadel/oidc/v3/pkg/oidc/grants/tokenexchange" ) // DelegationTokenRequest is an implementation of TokenExchangeRequest diff --git a/pkg/client/rp/device.go b/pkg/client/rp/device.go index 1fadd56..c2d1f8a 100644 --- a/pkg/client/rp/device.go +++ b/pkg/client/rp/device.go @@ -5,8 +5,8 @@ import ( "fmt" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/oidc" ) func newDeviceClientCredentialsRequest(scopes []string, rp RelyingParty) (*oidc.ClientCredentialsRequest, error) { diff --git a/pkg/client/rp/jwks.go b/pkg/client/rp/jwks.go index 0ccbad2..c44a267 100644 --- a/pkg/client/rp/jwks.go +++ b/pkg/client/rp/jwks.go @@ -9,9 +9,9 @@ import ( jose "github.com/go-jose/go-jose/v4" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) func NewRemoteKeySet(client *http.Client, jwksURL string, opts ...func(*remoteKeySet)) oidc.KeySet { diff --git a/pkg/client/rp/relying_party.go b/pkg/client/rp/relying_party.go index c2759a2..029a897 100644 --- a/pkg/client/rp/relying_party.go +++ b/pkg/client/rp/relying_party.go @@ -14,10 +14,10 @@ import ( "golang.org/x/oauth2" "golang.org/x/oauth2/clientcredentials" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/zitadel/logging" + "github.com/zitadel/oidc/v3/pkg/client" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) const ( @@ -541,7 +541,7 @@ func CodeExchangeHandler[C oidc.IDClaims](callback CodeExchangeCallback[C], rp R rp.CookieHandler().DeleteCookie(w, pkceCode) } if rp.Signer() != nil { - assertion, err := client.SignedJWTProfileAssertion(rp.OAuthConfig().ClientID, []string{rp.Issuer(), rp.OAuthConfig().Endpoint.TokenURL}, time.Hour, rp.Signer()) + assertion, err := client.SignedJWTProfileAssertion(rp.OAuthConfig().ClientID, []string{rp.Issuer()}, time.Hour, rp.Signer()) if err != nil { unauthorizedError(w, r, "failed to build assertion: "+err.Error(), state, rp) return diff --git a/pkg/client/rp/relying_party_test.go b/pkg/client/rp/relying_party_test.go index b3bb6ee..4c5a1b3 100644 --- a/pkg/client/rp/relying_party_test.go +++ b/pkg/client/rp/relying_party_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" "golang.org/x/oauth2" ) diff --git a/pkg/client/rp/tockenexchange.go b/pkg/client/rp/tockenexchange.go index aa2cf99..c8ca048 100644 --- a/pkg/client/rp/tockenexchange.go +++ b/pkg/client/rp/tockenexchange.go @@ -5,7 +5,7 @@ import ( "golang.org/x/oauth2" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc/grants/tokenexchange" + "github.com/zitadel/oidc/v3/pkg/oidc/grants/tokenexchange" ) // TokenExchangeRP extends the `RelyingParty` interface for the *draft* oauth2 `Token Exchange` diff --git a/pkg/client/rp/userinfo_example_test.go b/pkg/client/rp/userinfo_example_test.go index 78e014e..2cc5222 100644 --- a/pkg/client/rp/userinfo_example_test.go +++ b/pkg/client/rp/userinfo_example_test.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type UserInfo struct { diff --git a/pkg/client/rp/verifier.go b/pkg/client/rp/verifier.go index 0088b81..ca59454 100644 --- a/pkg/client/rp/verifier.go +++ b/pkg/client/rp/verifier.go @@ -6,8 +6,8 @@ import ( jose "github.com/go-jose/go-jose/v4" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // VerifyTokens implement the Token Response Validation as defined in OIDC specification diff --git a/pkg/client/rp/verifier_test.go b/pkg/client/rp/verifier_test.go index 38f5a4a..24d35af 100644 --- a/pkg/client/rp/verifier_test.go +++ b/pkg/client/rp/verifier_test.go @@ -5,11 +5,11 @@ import ( "testing" "time" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" jose "github.com/go-jose/go-jose/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" ) func TestVerifyTokens(t *testing.T) { diff --git a/pkg/client/rp/verifier_tokens_example_test.go b/pkg/client/rp/verifier_tokens_example_test.go index 7ae68d6..892eb23 100644 --- a/pkg/client/rp/verifier_tokens_example_test.go +++ b/pkg/client/rp/verifier_tokens_example_test.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rp" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/client/rp" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // MyCustomClaims extends the TokenClaims base, diff --git a/pkg/client/rs/introspect_example_test.go b/pkg/client/rs/introspect_example_test.go index 1f67d11..eac8be2 100644 --- a/pkg/client/rs/introspect_example_test.go +++ b/pkg/client/rs/introspect_example_test.go @@ -4,8 +4,8 @@ import ( "context" "fmt" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client/rs" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client/rs" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type IntrospectionResponse struct { diff --git a/pkg/client/rs/resource_server.go b/pkg/client/rs/resource_server.go index 993796e..962af7e 100644 --- a/pkg/client/rs/resource_server.go +++ b/pkg/client/rs/resource_server.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/client" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type ResourceServer interface { diff --git a/pkg/client/rs/resource_server_test.go b/pkg/client/rs/resource_server_test.go index afd7441..7a5ced9 100644 --- a/pkg/client/rs/resource_server_test.go +++ b/pkg/client/rs/resource_server_test.go @@ -4,9 +4,9 @@ import ( "context" "testing" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zitadel/oidc/v3/pkg/oidc" ) func TestNewResourceServer(t *testing.T) { diff --git a/pkg/client/tokenexchange/tokenexchange.go b/pkg/client/tokenexchange/tokenexchange.go index 9cc1328..61975a4 100644 --- a/pkg/client/tokenexchange/tokenexchange.go +++ b/pkg/client/tokenexchange/tokenexchange.go @@ -6,10 +6,10 @@ import ( "net/http" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/go-jose/go-jose/v4" + "github.com/zitadel/oidc/v3/pkg/client" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type TokenExchanger interface { diff --git a/pkg/crypto/hash.go b/pkg/crypto/hash.go index 14acdee..ab9f8c1 100644 --- a/pkg/crypto/hash.go +++ b/pkg/crypto/hash.go @@ -21,14 +21,6 @@ func GetHashAlgorithm(sigAlgorithm jose.SignatureAlgorithm) (hash.Hash, error) { return sha512.New384(), nil case jose.RS512, jose.ES512, jose.PS512: return sha512.New(), nil - - // There is no published spec for this yet, but we have confirmation it will get published. - // There is consensus here: https://bitbucket.org/openid/connect/issues/1125/_hash-algorithm-for-eddsa-id-tokens - // Currently Go and go-jose only supports the ed25519 curve key for EdDSA, so we can safely assume sha512 here. - // It is unlikely ed448 will ever be supported: https://github.com/golang/go/issues/29390 - case jose.EdDSA: - return sha512.New(), nil - default: return nil, fmt.Errorf("%w: %q", ErrUnsupportedAlgorithm, sigAlgorithm) } diff --git a/pkg/crypto/key_test.go b/pkg/crypto/key_test.go index a6fa493..8ed5cb5 100644 --- a/pkg/crypto/key_test.go +++ b/pkg/crypto/key_test.go @@ -10,7 +10,7 @@ import ( "github.com/go-jose/go-jose/v4" "github.com/stretchr/testify/assert" - zcrypto "git.christmann.info/LARA/zitadel-oidc/v3/pkg/crypto" + zcrypto "github.com/zitadel/oidc/v3/pkg/crypto" ) func TestBytesToPrivateKey(t *testing.T) { diff --git a/pkg/http/http.go b/pkg/http/http.go index aa0ff6f..33c5f15 100644 --- a/pkg/http/http.go +++ b/pkg/http/http.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" ) var DefaultHTTPClient = &http.Client{ diff --git a/pkg/oidc/code_challenge.go b/pkg/oidc/code_challenge.go index 0c593df..3296362 100644 --- a/pkg/oidc/code_challenge.go +++ b/pkg/oidc/code_challenge.go @@ -3,7 +3,7 @@ package oidc import ( "crypto/sha256" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/crypto" + "github.com/zitadel/oidc/v3/pkg/crypto" ) const ( diff --git a/pkg/oidc/discovery.go b/pkg/oidc/discovery.go index 62288d1..14fce5e 100644 --- a/pkg/oidc/discovery.go +++ b/pkg/oidc/discovery.go @@ -145,14 +145,6 @@ type DiscoveryConfiguration struct { // OPTermsOfServiceURI is a URL the OpenID Provider provides to the person registering the Client to read about OpenID Provider's terms of service. OPTermsOfServiceURI string `json:"op_tos_uri,omitempty"` - - // BackChannelLogoutSupported specifies whether the OP supports back-channel logout (https://openid.net/specs/openid-connect-backchannel-1_0.html), - // with true indicating support. If omitted, the default value is false. - BackChannelLogoutSupported bool `json:"backchannel_logout_supported,omitempty"` - - // BackChannelLogoutSessionSupported specifies whether the OP can pass a sid (session ID) Claim in the Logout Token to identify the RP session with the OP. - // If supported, the sid Claim is also included in ID Tokens issued by the OP. If omitted, the default value is false. - BackChannelLogoutSessionSupported bool `json:"backchannel_logout_session_supported,omitempty"` } type AuthMethod string diff --git a/pkg/oidc/error.go b/pkg/oidc/error.go index d93cf44..1100f73 100644 --- a/pkg/oidc/error.go +++ b/pkg/oidc/error.go @@ -133,7 +133,6 @@ type Error struct { ErrorType errorType `json:"error" schema:"error"` Description string `json:"error_description,omitempty" schema:"error_description,omitempty"` State string `json:"state,omitempty" schema:"state,omitempty"` - SessionState string `json:"session_state,omitempty" schema:"session_state,omitempty"` redirectDisabled bool `schema:"-"` returnParent bool `schema:"-"` } @@ -143,13 +142,11 @@ func (e *Error) MarshalJSON() ([]byte, error) { Error errorType `json:"error"` ErrorDescription string `json:"error_description,omitempty"` State string `json:"state,omitempty"` - SessionState string `json:"session_state,omitempty"` Parent string `json:"parent,omitempty"` }{ Error: e.ErrorType, ErrorDescription: e.Description, State: e.State, - SessionState: e.SessionState, } if e.returnParent { m.Parent = e.Parent.Error() @@ -179,8 +176,7 @@ func (e *Error) Is(target error) bool { } return e.ErrorType == t.ErrorType && (e.Description == t.Description || t.Description == "") && - (e.State == t.State || t.State == "") && - (e.SessionState == t.SessionState || t.SessionState == "") + (e.State == t.State || t.State == "") } func (e *Error) WithParent(err error) *Error { @@ -246,9 +242,6 @@ func (e *Error) LogValue() slog.Value { if e.State != "" { attrs = append(attrs, slog.String("state", e.State)) } - if e.SessionState != "" { - attrs = append(attrs, slog.String("session_state", e.SessionState)) - } if e.redirectDisabled { attrs = append(attrs, slog.Bool("redirect_disabled", e.redirectDisabled)) } diff --git a/pkg/oidc/keyset.go b/pkg/oidc/keyset.go index a8b89b0..833878d 100644 --- a/pkg/oidc/keyset.go +++ b/pkg/oidc/keyset.go @@ -6,7 +6,6 @@ import ( "crypto/ed25519" "crypto/rsa" "errors" - "strings" jose "github.com/go-jose/go-jose/v4" ) @@ -93,17 +92,17 @@ func FindMatchingKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (k } func algToKeyType(key any, alg string) bool { - if strings.HasPrefix(alg, "RS") || strings.HasPrefix(alg, "PS") { + switch alg[0] { + case 'R', 'P': _, ok := key.(*rsa.PublicKey) return ok - } - if strings.HasPrefix(alg, "ES") { + case 'E': _, ok := key.(*ecdsa.PublicKey) return ok - } - if alg == string(jose.EdDSA) { - _, ok := key.(ed25519.PublicKey) + case 'O': + _, ok := key.(*ed25519.PublicKey) return ok + default: + return false } - return false } diff --git a/pkg/oidc/session.go b/pkg/oidc/session.go index 39f9f08..b470d1e 100644 --- a/pkg/oidc/session.go +++ b/pkg/oidc/session.go @@ -1,12 +1,10 @@ package oidc // EndSessionRequest for the RP-Initiated Logout according to: -// https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout +//https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout type EndSessionRequest struct { - IdTokenHint string `schema:"id_token_hint"` - LogoutHint string `schema:"logout_hint"` - ClientID string `schema:"client_id"` - PostLogoutRedirectURI string `schema:"post_logout_redirect_uri"` - State string `schema:"state"` - UILocales Locales `schema:"ui_locales"` + IdTokenHint string `schema:"id_token_hint"` + ClientID string `schema:"client_id"` + PostLogoutRedirectURI string `schema:"post_logout_redirect_uri"` + State string `schema:"state"` } diff --git a/pkg/oidc/token.go b/pkg/oidc/token.go index 4b43dcb..5b18dac 100644 --- a/pkg/oidc/token.go +++ b/pkg/oidc/token.go @@ -10,7 +10,7 @@ import ( "github.com/muhlemmer/gu" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/crypto" + "github.com/zitadel/oidc/v3/pkg/crypto" ) const ( @@ -117,7 +117,6 @@ func NewAccessTokenClaims(issuer, subject string, audience []string, expiration Expiration: FromTime(expiration), IssuedAt: FromTime(now), NotBefore: FromTime(now), - ClientID: clientID, JWTID: jwtid, }, } @@ -230,13 +229,12 @@ func (c *ActorClaims) UnmarshalJSON(data []byte) error { } type AccessTokenResponse struct { - AccessToken string `json:"access_token,omitempty" schema:"access_token,omitempty"` - TokenType string `json:"token_type,omitempty" schema:"token_type,omitempty"` - RefreshToken string `json:"refresh_token,omitempty" schema:"refresh_token,omitempty"` - ExpiresIn uint64 `json:"expires_in,omitempty" schema:"expires_in,omitempty"` - IDToken string `json:"id_token,omitempty" schema:"id_token,omitempty"` - State string `json:"state,omitempty" schema:"state,omitempty"` - Scope SpaceDelimitedArray `json:"scope,omitempty" schema:"scope,omitempty"` + AccessToken string `json:"access_token,omitempty" schema:"access_token,omitempty"` + TokenType string `json:"token_type,omitempty" schema:"token_type,omitempty"` + RefreshToken string `json:"refresh_token,omitempty" schema:"refresh_token,omitempty"` + ExpiresIn uint64 `json:"expires_in,omitempty" schema:"expires_in,omitempty"` + IDToken string `json:"id_token,omitempty" schema:"id_token,omitempty"` + State string `json:"state,omitempty" schema:"state,omitempty"` } type JWTProfileAssertionClaims struct { @@ -383,40 +381,3 @@ type TokenExchangeResponse struct { // if the requested_token_type was Access Token and scope contained openid. IDToken string `json:"id_token,omitempty"` } - -type LogoutTokenClaims struct { - Issuer string `json:"iss,omitempty"` - Subject string `json:"sub,omitempty"` - Audience Audience `json:"aud,omitempty"` - IssuedAt Time `json:"iat,omitempty"` - Expiration Time `json:"exp,omitempty"` - JWTID string `json:"jti,omitempty"` - Events map[string]any `json:"events,omitempty"` - SessionID string `json:"sid,omitempty"` - Claims map[string]any `json:"-"` -} - -type ltcAlias LogoutTokenClaims - -func (i *LogoutTokenClaims) MarshalJSON() ([]byte, error) { - return mergeAndMarshalClaims((*ltcAlias)(i), i.Claims) -} - -func (i *LogoutTokenClaims) UnmarshalJSON(data []byte) error { - return unmarshalJSONMulti(data, (*ltcAlias)(i), &i.Claims) -} - -func NewLogoutTokenClaims(issuer, subject string, audience Audience, expiration time.Time, jwtID, sessionID string, skew time.Duration) *LogoutTokenClaims { - return &LogoutTokenClaims{ - Issuer: issuer, - Subject: subject, - Audience: audience, - IssuedAt: FromTime(time.Now().Add(-skew)), - Expiration: FromTime(expiration), - JWTID: jwtID, - Events: map[string]any{ - "http://schemas.openid.net/event/backchannel-logout": struct{}{}, - }, - SessionID: sessionID, - } -} diff --git a/pkg/oidc/token_request.go b/pkg/oidc/token_request.go index dadb205..f3b2ec4 100644 --- a/pkg/oidc/token_request.go +++ b/pkg/oidc/token_request.go @@ -72,10 +72,10 @@ type AccessTokenRequest struct { Code string `schema:"code"` RedirectURI string `schema:"redirect_uri"` ClientID string `schema:"client_id"` - ClientSecret string `schema:"client_secret,omitempty"` - CodeVerifier string `schema:"code_verifier,omitempty"` - ClientAssertion string `schema:"client_assertion,omitempty"` - ClientAssertionType string `schema:"client_assertion_type,omitempty"` + ClientSecret string `schema:"client_secret"` + CodeVerifier string `schema:"code_verifier"` + ClientAssertion string `schema:"client_assertion"` + ClientAssertionType string `schema:"client_assertion_type"` } func (a *AccessTokenRequest) GrantType() GrantType { diff --git a/pkg/oidc/token_test.go b/pkg/oidc/token_test.go index 621cdbc..ccc3467 100644 --- a/pkg/oidc/token_test.go +++ b/pkg/oidc/token_test.go @@ -145,7 +145,6 @@ func TestNewAccessTokenClaims(t *testing.T) { Subject: "hello@me.com", Audience: Audience{"foo"}, Expiration: 12345, - ClientID: "foo", JWTID: "900", }, } @@ -242,39 +241,3 @@ func TestIDTokenClaims_GetUserInfo(t *testing.T) { got := idTokenData.GetUserInfo() assert.Equal(t, want, got) } - -func TestNewLogoutTokenClaims(t *testing.T) { - want := &LogoutTokenClaims{ - Issuer: "zitadel", - Subject: "hello@me.com", - Audience: Audience{"foo", "just@me.com"}, - Expiration: 12345, - JWTID: "jwtID", - Events: map[string]any{ - "http://schemas.openid.net/event/backchannel-logout": struct{}{}, - }, - SessionID: "sessionID", - Claims: nil, - } - - got := NewLogoutTokenClaims( - want.Issuer, - want.Subject, - want.Audience, - want.Expiration.AsTime(), - want.JWTID, - want.SessionID, - 1*time.Second, - ) - - // test if the dynamic timestamp is around now, - // allowing for a delta of 1, just in case we flip on - // either side of a second boundry. - nowMinusSkew := NowTime() - 1 - assert.InDelta(t, int64(nowMinusSkew), int64(got.IssuedAt), 1) - - // Make equal not fail on dynamic timestamp - got.IssuedAt = 0 - - assert.Equal(t, want, got) -} diff --git a/pkg/oidc/types.go b/pkg/oidc/types.go index 5d063b1..e7292e6 100644 --- a/pkg/oidc/types.go +++ b/pkg/oidc/types.go @@ -35,17 +35,6 @@ func (a *Audience) UnmarshalJSON(text []byte) error { return nil } -func (a *Audience) MarshalJSON() ([]byte, error) { - len := len(*a) - if len > 1 { - return json.Marshal(*a) - } else if len == 1 { - return json.Marshal((*a)[0]) - } - - return nil, errors.New("aud is empty") -} - type Display string func (d *Display) UnmarshalText(text []byte) error { @@ -93,9 +82,6 @@ func (l *Locale) MarshalJSON() ([]byte, error) { // to an empty value (language "und") and no error will be returned. // This state can be checked with the `l.Tag().IsRoot()` method. func (l *Locale) UnmarshalJSON(data []byte) error { - if len(data) == 0 || string(data) == "\"\"" { - return nil - } err := json.Unmarshal(data, &l.tag) if err == nil { return nil @@ -126,14 +112,6 @@ func ParseLocales(locales []string) Locales { return out } -func (l Locales) String() string { - tags := make([]string, len(l)) - for i, tag := range l { - tags[i] = tag.String() - } - return strings.Join(tags, " ") -} - // UnmarshalText implements the [encoding.TextUnmarshaler] interface. // It decodes an unquoted space seperated string into Locales. // Undefined language tags in the input are ignored and ommited from @@ -250,9 +228,6 @@ func NewEncoder() *schema.Encoder { e.RegisterEncoder(SpaceDelimitedArray{}, func(value reflect.Value) string { return value.Interface().(SpaceDelimitedArray).String() }) - e.RegisterEncoder(Locales{}, func(value reflect.Value) string { - return value.Interface().(Locales).String() - }) return e } diff --git a/pkg/oidc/types_test.go b/pkg/oidc/types_test.go index 53a9779..df93a73 100644 --- a/pkg/oidc/types_test.go +++ b/pkg/oidc/types_test.go @@ -217,30 +217,6 @@ func TestLocale_UnmarshalJSON(t *testing.T) { want dst wantErr bool }{ - { - name: "value not present", - input: `{}`, - wantErr: false, - want: dst{ - Locale: nil, - }, - }, - { - name: "null", - input: `{"locale": null}`, - wantErr: false, - want: dst{ - Locale: nil, - }, - }, - { - name: "empty, ignored", - input: `{"locale": ""}`, - wantErr: false, - want: dst{ - Locale: &Locale{}, - }, - }, { name: "afrikaans, ok", input: `{"locale": "af"}`, @@ -261,17 +237,16 @@ func TestLocale_UnmarshalJSON(t *testing.T) { wantErr: true, }, } + for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got dst - err := json.Unmarshal([]byte(tt.input), &got) - if tt.wantErr { - require.Error(t, err) - return - } - require.NoError(t, err) - assert.Equal(t, tt.want, got) - }) + var got dst + err := json.Unmarshal([]byte(tt.input), &got) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, got) } } diff --git a/pkg/oidc/verifier.go b/pkg/oidc/verifier.go index d5e0213..cb66676 100644 --- a/pkg/oidc/verifier.go +++ b/pkg/oidc/verifier.go @@ -7,11 +7,12 @@ import ( "encoding/json" "errors" "fmt" - "slices" "strings" "time" jose "github.com/go-jose/go-jose/v4" + + str "github.com/zitadel/oidc/v3/pkg/strings" ) type Claims interface { @@ -40,7 +41,6 @@ type IDClaims interface { var ( ErrParse = errors.New("parsing of request failed") ErrIssuerInvalid = errors.New("issuer does not match") - ErrDiscoveryFailed = errors.New("OpenID Provider Configuration Discovery has failed") ErrSubjectMissing = errors.New("subject missing") ErrAudience = errors.New("audience is not valid") ErrAzpMissing = errors.New("authorized party is not set. If Token is valid for multiple audiences, azp must not be empty") @@ -83,7 +83,7 @@ type ACRVerifier func(string) error // if none of the provided values matches the acr claim func DefaultACRVerifier(possibleValues []string) ACRVerifier { return func(acr string) error { - if !slices.Contains(possibleValues, acr) { + if !str.Contains(possibleValues, acr) { return fmt.Errorf("expected one of: %v, got: %q", possibleValues, acr) } return nil @@ -122,7 +122,7 @@ func CheckIssuer(claims Claims, issuer string) error { } func CheckAudience(claims Claims, clientID string) error { - if !slices.Contains(claims.GetAudience(), clientID) { + if !str.Contains(claims.GetAudience(), clientID) { return fmt.Errorf("%w: Audience must contain client_id %q", ErrAudience, clientID) } diff --git a/pkg/oidc/verifier_parse_test.go b/pkg/oidc/verifier_parse_test.go index 9cf5c1e..105650f 100644 --- a/pkg/oidc/verifier_parse_test.go +++ b/pkg/oidc/verifier_parse_test.go @@ -5,10 +5,10 @@ import ( "encoding/json" "testing" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" ) func TestParseToken(t *testing.T) { diff --git a/pkg/op/auth_request.go b/pkg/op/auth_request.go index b1434cc..fe73180 100644 --- a/pkg/op/auth_request.go +++ b/pkg/op/auth_request.go @@ -15,9 +15,10 @@ import ( "strings" "time" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/bmatcuk/doublestar/v4" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" + str "github.com/zitadel/oidc/v3/pkg/strings" ) type AuthRequest interface { @@ -38,13 +39,6 @@ type AuthRequest interface { Done() bool } -// AuthRequestSessionState should be implemented if [OpenID Connect Session Management](https://openid.net/specs/openid-connect-session-1_0.html) is supported -type AuthRequestSessionState interface { - // GetSessionState returns session_state. - // session_state is related to OpenID Connect Session Management. - GetSessionState() string -} - type Authorizer interface { Storage() Storage Decoder() httphelper.Decoder @@ -62,12 +56,6 @@ type AuthorizeValidator interface { ValidateAuthRequest(context.Context, *oidc.AuthRequest, Storage, *IDTokenHintVerifier) (string, error) } -type CodeResponseType struct { - Code string `schema:"code"` - State string `schema:"state,omitempty"` - SessionState string `schema:"session_state,omitempty"` -} - func authorizeHandler(authorizer Authorizer) func(http.ResponseWriter, *http.Request) { return func(w http.ResponseWriter, r *http.Request) { Authorize(w, r, authorizer) @@ -95,29 +83,21 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { if authReq.RequestParam != "" && authorizer.RequestObjectSupported() { err = ParseRequestObject(ctx, authReq, authorizer.Storage(), IssuerFromContext(ctx)) if err != nil { - AuthRequestError(w, r, nil, err, authorizer) + AuthRequestError(w, r, authReq, err, authorizer) return } } if authReq.ClientID == "" { - AuthRequestError(w, r, nil, fmt.Errorf("auth request is missing client_id"), authorizer) + AuthRequestError(w, r, authReq, fmt.Errorf("auth request is missing client_id"), authorizer) return } if authReq.RedirectURI == "" { - AuthRequestError(w, r, nil, fmt.Errorf("auth request is missing redirect_uri"), authorizer) + AuthRequestError(w, r, authReq, fmt.Errorf("auth request is missing redirect_uri"), authorizer) return } - - var client Client - validation := func(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, verifier *IDTokenHintVerifier) (sub string, err error) { - client, err = authorizer.Storage().GetClientByClientID(ctx, authReq.ClientID) - if err != nil { - return "", oidc.ErrInvalidRequestRedirectURI().WithDescription("unable to retrieve client by id").WithParent(err) - } - return ValidateAuthRequestClient(ctx, authReq, client, verifier) - } - if validator, ok := authorizer.(AuthorizeValidator); ok { - validation = validator.ValidateAuthRequest + validation := ValidateAuthRequest + if validater, ok := authorizer.(AuthorizeValidator); ok { + validation = validater.ValidateAuthRequest } userID, err := validation(ctx, authReq, authorizer.Storage(), authorizer.IDTokenHintVerifier(ctx)) if err != nil { @@ -133,6 +113,11 @@ func Authorize(w http.ResponseWriter, r *http.Request, authorizer Authorizer) { AuthRequestError(w, r, authReq, oidc.DefaultToServerError(err, "unable to save auth request"), authorizer) return } + client, err := authorizer.Storage().GetClientByClientID(ctx, req.GetClientID()) + if err != nil { + AuthRequestError(w, r, req, oidc.DefaultToServerError(err, "unable to retrieve client by id"), authorizer) + return + } RedirectToLogin(req.GetID(), client, w, r) } @@ -168,7 +153,7 @@ func ParseRequestObject(ctx context.Context, authReq *oidc.AuthRequest, storage if requestObject.Issuer != requestObject.ClientID { return oidc.ErrInvalidRequest().WithDescription("missing or wrong issuer in request") } - if !slices.Contains(requestObject.Audience, issuer) { + if !str.Contains(requestObject.Audience, issuer) { return oidc.ErrInvalidRequest().WithDescription("issuer missing in audience") } keySet := &jwtProfileKeySet{storage: storage, clientID: requestObject.Issuer} @@ -182,7 +167,7 @@ func ParseRequestObject(ctx context.Context, authReq *oidc.AuthRequest, storage // CopyRequestObjectToAuthRequest overwrites present values from the Request Object into the auth request // and clears the `RequestParam` of the auth request func CopyRequestObjectToAuthRequest(authReq *oidc.AuthRequest, requestObject *oidc.RequestObject) { - if slices.Contains(authReq.Scopes, oidc.ScopeOpenID) && len(requestObject.Scopes) > 0 { + if str.Contains(authReq.Scopes, oidc.ScopeOpenID) && len(requestObject.Scopes) > 0 { authReq.Scopes = requestObject.Scopes } if requestObject.RedirectURI != "" { @@ -227,37 +212,26 @@ func CopyRequestObjectToAuthRequest(authReq *oidc.AuthRequest, requestObject *oi authReq.RequestParam = "" } -// ValidateAuthRequest validates the authorize parameters and returns the userID of the id_token_hint if passed. -// -// Deprecated: Use [ValidateAuthRequestClient] to prevent querying for the Client twice. +// ValidateAuthRequest validates the authorize parameters and returns the userID of the id_token_hint if passed func ValidateAuthRequest(ctx context.Context, authReq *oidc.AuthRequest, storage Storage, verifier *IDTokenHintVerifier) (sub string, err error) { ctx, span := tracer.Start(ctx, "ValidateAuthRequest") defer span.End() - client, err := storage.GetClientByClientID(ctx, authReq.ClientID) - if err != nil { - return "", oidc.ErrInvalidRequestRedirectURI().WithDescription("unable to retrieve client by id").WithParent(err) - } - return ValidateAuthRequestClient(ctx, authReq, client, verifier) -} - -// ValidateAuthRequestClient validates the Auth request against the passed client. -// If id_token_hint is part of the request, the subject of the token is returned. -func ValidateAuthRequestClient(ctx context.Context, authReq *oidc.AuthRequest, client Client, verifier *IDTokenHintVerifier) (sub string, err error) { - ctx, span := tracer.Start(ctx, "ValidateAuthRequestClient") - defer span.End() - - if err := ValidateAuthReqRedirectURI(client, authReq.RedirectURI, authReq.ResponseType); err != nil { - return "", err - } authReq.MaxAge, err = ValidateAuthReqPrompt(authReq.Prompt, authReq.MaxAge) if err != nil { return "", err } + client, err := storage.GetClientByClientID(ctx, authReq.ClientID) + if err != nil { + return "", oidc.DefaultToServerError(err, "unable to retrieve client by id") + } authReq.Scopes, err = ValidateAuthReqScopes(client, authReq.Scopes) if err != nil { return "", err } + if err := ValidateAuthReqRedirectURI(client, authReq.RedirectURI, authReq.ResponseType); err != nil { + return "", err + } if err := ValidateAuthReqResponseType(client, authReq.ResponseType); err != nil { return "", err } @@ -300,7 +274,7 @@ func ValidateAuthReqScopes(client Client, scopes []string) ([]string, error) { // checkURIAgainstRedirects just checks aginst the valid redirect URIs and ignores // other factors. func checkURIAgainstRedirects(client Client, uri string) error { - if slices.Contains(client.RedirectURIs(), uri) { + if str.Contains(client.RedirectURIs(), uri) { return nil } if globClient, ok := client.(HasRedirectGlobs); ok { @@ -325,12 +299,12 @@ func ValidateAuthReqRedirectURI(client Client, uri string, responseType oidc.Res return oidc.ErrInvalidRequestRedirectURI().WithDescription("The redirect_uri is missing in the request. " + "Please ensure it is added to the request. If you have any questions, you may contact the administrator of the application.") } - if client.ApplicationType() == ApplicationTypeNative { - return validateAuthReqRedirectURINative(client, uri) - } if strings.HasPrefix(uri, "https://") { return checkURIAgainstRedirects(client, uri) } + if client.ApplicationType() == ApplicationTypeNative { + return validateAuthReqRedirectURINative(client, uri) + } if err := checkURIAgainstRedirects(client, uri); err != nil { return err } @@ -351,15 +325,12 @@ func ValidateAuthReqRedirectURI(client Client, uri string, responseType oidc.Res // ValidateAuthReqRedirectURINative validates the passed redirect_uri and response_type to the registered uris and client type func validateAuthReqRedirectURINative(client Client, uri string) error { parsedURL, isLoopback := HTTPLoopbackOrLocalhost(uri) - isCustomSchema := !(strings.HasPrefix(uri, "http://") || strings.HasPrefix(uri, "https://")) + isCustomSchema := !strings.HasPrefix(uri, "http://") if err := checkURIAgainstRedirects(client, uri); err == nil { if client.DevMode() { return nil } - if !isLoopback && strings.HasPrefix(uri, "https://") { - return nil - } - // The RedirectURIs are only valid for native clients when localhost or non-"http://" and "https://" + // The RedirectURIs are only valid for native clients when localhost or non-"http://" if isLoopback || isCustomSchema { return nil } @@ -389,11 +360,11 @@ func HTTPLoopbackOrLocalhost(rawURL string) (*url.URL, bool) { if err != nil { return nil, false } - if parsedURL.Scheme == "http" || parsedURL.Scheme == "https" { - hostName := parsedURL.Hostname() - return parsedURL, hostName == "localhost" || net.ParseIP(hostName).IsLoopback() + if parsedURL.Scheme != "http" { + return nil, false } - return nil, false + hostName := parsedURL.Hostname() + return parsedURL, hostName == "localhost" || net.ParseIP(hostName).IsLoopback() } // ValidateAuthReqResponseType validates the passed response_type to the registered response types @@ -483,70 +454,41 @@ func AuthResponse(authReq AuthRequest, authorizer Authorizer, w http.ResponseWri AuthResponseToken(w, r, authReq, authorizer, client) } -// AuthResponseCode handles the creation of a successful authentication response using an authorization code +// AuthResponseCode creates the successful code authentication response func AuthResponseCode(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer) { ctx, span := tracer.Start(r.Context(), "AuthResponseCode") - defer span.End() r = r.WithContext(ctx) + defer span.End() - var err error - if authReq.GetResponseMode() == oidc.ResponseModeFormPost { - err = handleFormPostResponse(w, r, authReq, authorizer) - } else { - err = handleRedirectResponse(w, r, authReq, authorizer) - } - + code, err := CreateAuthRequestCode(r.Context(), authReq, authorizer.Storage(), authorizer.Crypto()) if err != nil { AuthRequestError(w, r, authReq, err, authorizer) + return + } + codeResponse := struct { + Code string `schema:"code"` + State string `schema:"state,omitempty"` + }{ + Code: code, + State: authReq.GetState(), } -} -// handleFormPostResponse processes the authentication response using form post method -func handleFormPostResponse(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer) error { - codeResponse, err := BuildAuthResponseCodeResponsePayload(r.Context(), authReq, authorizer) + if authReq.GetResponseMode() == oidc.ResponseModeFormPost { + err := AuthResponseFormPost(w, authReq.GetRedirectURI(), &codeResponse, authorizer.Encoder()) + if err != nil { + AuthRequestError(w, r, authReq, err, authorizer) + return + } + + return + } + + callback, err := AuthResponseURL(authReq.GetRedirectURI(), authReq.GetResponseType(), authReq.GetResponseMode(), &codeResponse, authorizer.Encoder()) if err != nil { - return err + AuthRequestError(w, r, authReq, err, authorizer) + return } - return AuthResponseFormPost(w, authReq.GetRedirectURI(), codeResponse, authorizer.Encoder()) -} - -// handleRedirectResponse processes the authentication response using the redirect method -func handleRedirectResponse(w http.ResponseWriter, r *http.Request, authReq AuthRequest, authorizer Authorizer) error { - callbackURL, err := BuildAuthResponseCallbackURL(r.Context(), authReq, authorizer) - if err != nil { - return err - } - http.Redirect(w, r, callbackURL, http.StatusFound) - return nil -} - -// BuildAuthResponseCodeResponsePayload generates the authorization code response payload for the authentication request -func BuildAuthResponseCodeResponsePayload(ctx context.Context, authReq AuthRequest, authorizer Authorizer) (*CodeResponseType, error) { - code, err := CreateAuthRequestCode(ctx, authReq, authorizer.Storage(), authorizer.Crypto()) - if err != nil { - return nil, err - } - - sessionState := "" - if authRequestSessionState, ok := authReq.(AuthRequestSessionState); ok { - sessionState = authRequestSessionState.GetSessionState() - } - - return &CodeResponseType{ - Code: code, - State: authReq.GetState(), - SessionState: sessionState, - }, nil -} - -// BuildAuthResponseCallbackURL generates the callback URL for a successful authorization code response -func BuildAuthResponseCallbackURL(ctx context.Context, authReq AuthRequest, authorizer Authorizer) (string, error) { - codeResponse, err := BuildAuthResponseCodeResponsePayload(ctx, authReq, authorizer) - if err != nil { - return "", err - } - - return AuthResponseURL(authReq.GetRedirectURI(), authReq.GetResponseType(), authReq.GetResponseMode(), codeResponse, authorizer.Encoder()) + http.Redirect(w, r, callback, http.StatusFound) } // AuthResponseToken creates the successful token(s) authentication response diff --git a/pkg/op/auth_request_test.go b/pkg/op/auth_request_test.go index d1ea965..6b4af17 100644 --- a/pkg/op/auth_request_test.go +++ b/pkg/op/auth_request_test.go @@ -11,15 +11,15 @@ import ( "reflect" "testing" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/storage" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op/mock" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zitadel/oidc/v3/example/server/storage" + tu "github.com/zitadel/oidc/v3/internal/testutil" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op/mock" "github.com/zitadel/schema" ) @@ -433,24 +433,6 @@ func TestValidateAuthReqRedirectURI(t *testing.T) { }, false, }, - { - "code flow registered https loopback v4 native ok", - args{ - "https://127.0.0.1:4200/callback", - mock.NewClientWithConfig(t, []string{"https://127.0.0.1/callback"}, op.ApplicationTypeNative, nil, false), - oidc.ResponseTypeCode, - }, - false, - }, - { - "code flow registered https loopback v6 native ok", - args{ - "https://[::1]:4200/callback", - mock.NewClientWithConfig(t, []string{"https://[::1]/callback"}, op.ApplicationTypeNative, nil, false), - oidc.ResponseTypeCode, - }, - false, - }, { "code flow unregistered http native fails", args{ @@ -1090,34 +1072,6 @@ func TestAuthResponseCode(t *testing.T) { wantBody: "", }, }, - { - name: "success with state and session_state", - args: args{ - authReq: &storage.AuthRequestWithSessionState{ - AuthRequest: &storage.AuthRequest{ - ID: "id1", - TransferState: "state1", - }, - SessionState: "session_state1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) - return authorizer - }, - }, - res: res{ - wantCode: http.StatusFound, - wantLocationHeader: "/auth/callback/?code=id1&session_state=session_state1&state=state1", - wantBody: "", - }, - }, { name: "success without state", // reproduce issue #415 args: args{ @@ -1225,133 +1179,6 @@ func Test_parseAuthorizeCallbackRequest(t *testing.T) { } } -func TestBuildAuthResponseCodeResponsePayload(t *testing.T) { - type args struct { - authReq op.AuthRequest - authorizer func(*testing.T) op.Authorizer - } - type res struct { - wantCode string - wantState string - wantSessionState string - wantErr bool - } - tests := []struct { - name string - args args - res res - }{ - { - name: "create code error", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{ - returnErr: io.ErrClosedPipe, - }) - return authorizer - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "success with state", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - TransferState: "state1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - return authorizer - }, - }, - res: res{ - wantCode: "id1", - wantState: "state1", - }, - }, - { - name: "success without state", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - TransferState: "", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - return authorizer - }, - }, - res: res{ - wantCode: "id1", - wantState: "", - }, - }, - { - name: "success with session_state", - args: args{ - authReq: &storage.AuthRequestWithSessionState{ - AuthRequest: &storage.AuthRequest{ - ID: "id1", - TransferState: "state1", - }, - SessionState: "session_state1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - return authorizer - }, - }, - res: res{ - wantCode: "id1", - wantState: "state1", - wantSessionState: "session_state1", - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := op.BuildAuthResponseCodeResponsePayload(context.Background(), tt.args.authReq, tt.args.authorizer(t)) - if tt.res.wantErr { - assert.Error(t, err) - return - } - require.NoError(t, err) - assert.Equal(t, tt.res.wantCode, got.Code) - assert.Equal(t, tt.res.wantState, got.State) - assert.Equal(t, tt.res.wantSessionState, got.SessionState) - }) - } -} - func TestValidateAuthReqIDTokenHint(t *testing.T) { token, _ := tu.ValidIDToken() tests := []struct { @@ -1382,231 +1209,3 @@ func TestValidateAuthReqIDTokenHint(t *testing.T) { }) } } - -func TestBuildAuthResponseCallbackURL(t *testing.T) { - type args struct { - authReq op.AuthRequest - authorizer func(*testing.T) op.Authorizer - } - type res struct { - wantURL string - wantErr bool - } - tests := []struct { - name string - args args - res res - }{ - { - name: "error when generating code response", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{ - returnErr: io.ErrClosedPipe, - }) - return authorizer - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "error when generating callback URL", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - CallbackURI: "://invalid-url", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) - return authorizer - }, - }, - res: res{ - wantErr: true, - }, - }, - { - name: "success with state", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - CallbackURI: "https://example.com/callback", - TransferState: "state1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) - return authorizer - }, - }, - res: res{ - wantURL: "https://example.com/callback?code=id1&state=state1", - wantErr: false, - }, - }, - { - name: "success without state", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - CallbackURI: "https://example.com/callback", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) - return authorizer - }, - }, - res: res{ - wantURL: "https://example.com/callback?code=id1", - wantErr: false, - }, - }, - { - name: "success with session_state", - args: args{ - authReq: &storage.AuthRequestWithSessionState{ - AuthRequest: &storage.AuthRequest{ - ID: "id1", - CallbackURI: "https://example.com/callback", - TransferState: "state1", - }, - SessionState: "session_state1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) - return authorizer - }, - }, - res: res{ - wantURL: "https://example.com/callback?code=id1&session_state=session_state1&state=state1", - wantErr: false, - }, - }, - { - name: "success with existing query parameters", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - CallbackURI: "https://example.com/callback?param=value", - TransferState: "state1", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) - return authorizer - }, - }, - res: res{ - wantURL: "https://example.com/callback?param=value&code=id1&state=state1", - wantErr: false, - }, - }, - { - name: "success with fragment response mode", - args: args{ - authReq: &storage.AuthRequest{ - ID: "id1", - CallbackURI: "https://example.com/callback", - TransferState: "state1", - ResponseMode: "fragment", - }, - authorizer: func(t *testing.T) op.Authorizer { - ctrl := gomock.NewController(t) - storage := mock.NewMockStorage(ctrl) - storage.EXPECT().SaveAuthCode(gomock.Any(), "id1", "id1") - - authorizer := mock.NewMockAuthorizer(ctrl) - authorizer.EXPECT().Storage().Return(storage) - authorizer.EXPECT().Crypto().Return(&mockCrypto{}) - authorizer.EXPECT().Encoder().Return(schema.NewEncoder()) - return authorizer - }, - }, - res: res{ - wantURL: "https://example.com/callback#code=id1&state=state1", - wantErr: false, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := op.BuildAuthResponseCallbackURL(context.Background(), tt.args.authReq, tt.args.authorizer(t)) - if tt.res.wantErr { - assert.Error(t, err) - return - } - require.NoError(t, err) - - if tt.res.wantURL != "" { - // Parse the URLs to compare components instead of direct string comparison - expectedURL, err := url.Parse(tt.res.wantURL) - require.NoError(t, err) - actualURL, err := url.Parse(got) - require.NoError(t, err) - - // Compare the base parts (scheme, host, path) - assert.Equal(t, expectedURL.Scheme, actualURL.Scheme) - assert.Equal(t, expectedURL.Host, actualURL.Host) - assert.Equal(t, expectedURL.Path, actualURL.Path) - - // Compare the fragment if any - assert.Equal(t, expectedURL.Fragment, actualURL.Fragment) - - // For query parameters, compare them independently of order - expectedQuery := expectedURL.Query() - actualQuery := actualURL.Query() - - assert.Equal(t, len(expectedQuery), len(actualQuery), "Query parameter count does not match") - - for key, expectedValues := range expectedQuery { - actualValues, exists := actualQuery[key] - assert.True(t, exists, "Expected query parameter %s not found", key) - assert.ElementsMatch(t, expectedValues, actualValues, "Values for parameter %s don't match", key) - } - } - }) - } -} diff --git a/pkg/op/client.go b/pkg/op/client.go index a4f44d3..913944c 100644 --- a/pkg/op/client.go +++ b/pkg/op/client.go @@ -7,8 +7,8 @@ import ( "net/url" "time" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) //go:generate go get github.com/dmarkham/enumer diff --git a/pkg/op/client_test.go b/pkg/op/client_test.go index b416630..b772ba5 100644 --- a/pkg/op/client_test.go +++ b/pkg/op/client_test.go @@ -10,13 +10,13 @@ import ( "strings" "testing" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op/mock" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op/mock" "github.com/zitadel/schema" ) diff --git a/pkg/op/config.go b/pkg/op/config.go index b271765..9fec7cc 100644 --- a/pkg/op/config.go +++ b/pkg/op/config.go @@ -30,7 +30,6 @@ type Configuration interface { EndSessionEndpoint() *Endpoint KeysEndpoint() *Endpoint DeviceAuthorizationEndpoint() *Endpoint - CheckSessionIframe() *Endpoint AuthMethodPostSupported() bool CodeMethodS256Supported() bool @@ -50,9 +49,6 @@ type Configuration interface { SupportedUILocales() []language.Tag DeviceAuthorization() DeviceAuthorizationConfig - - BackChannelLogoutSupported() bool - BackChannelLogoutSessionSupported() bool } type IssuerFromRequest func(r *http.Request) string diff --git a/pkg/op/crypto.go b/pkg/op/crypto.go index 01aaad3..6ab1e0a 100644 --- a/pkg/op/crypto.go +++ b/pkg/op/crypto.go @@ -1,7 +1,7 @@ package op import ( - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/crypto" + "github.com/zitadel/oidc/v3/pkg/crypto" ) type Crypto interface { diff --git a/pkg/op/device.go b/pkg/op/device.go index 866cbc4..11638b0 100644 --- a/pkg/op/device.go +++ b/pkg/op/device.go @@ -9,12 +9,12 @@ import ( "math/big" "net/http" "net/url" - "slices" "strings" "time" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" + strs "github.com/zitadel/oidc/v3/pkg/strings" ) type DeviceAuthorizationConfig struct { @@ -91,7 +91,10 @@ func createDeviceAuthorization(ctx context.Context, req *oidc.DeviceAuthorizatio } config := o.DeviceAuthorization() - deviceCode, _ := NewDeviceCode(RecommendedDeviceCodeBytes) + deviceCode, err := NewDeviceCode(RecommendedDeviceCodeBytes) + if err != nil { + return nil, NewStatusError(err, http.StatusInternalServerError) + } userCode, err := NewUserCode([]rune(config.UserCode.CharSet), config.UserCode.CharAmount, config.UserCode.DashInterval) if err != nil { return nil, NewStatusError(err, http.StatusInternalServerError) @@ -160,14 +163,11 @@ func ParseDeviceCodeRequest(r *http.Request, o OpenIDProvider) (*oidc.DeviceAuth // results in a 22 character base64 encoded string. const RecommendedDeviceCodeBytes = 16 -// NewDeviceCode generates a new cryptographically secure device code as a base64 encoded string. -// The length of the string is nBytes * 4 / 3. -// An error is never returned. -// -// TODO(v4): change return type to string alone. func NewDeviceCode(nBytes int) (string, error) { bytes := make([]byte, nBytes) - rand.Read(bytes) + if _, err := rand.Read(bytes); err != nil { + return "", fmt.Errorf("%w getting entropy for device code", err) + } return base64.RawURLEncoding.EncodeToString(bytes), nil } @@ -276,7 +276,7 @@ func (r *DeviceAuthorizationState) GetAMR() []string { } func (r *DeviceAuthorizationState) GetAudience() []string { - if !slices.Contains(r.Audience, r.ClientID) { + if !strs.Contains(r.Audience, r.ClientID) { r.Audience = append(r.Audience, r.ClientID) } return r.Audience @@ -344,11 +344,10 @@ func CreateDeviceTokenResponse(ctx context.Context, tokenRequest TokenRequest, c RefreshToken: refreshToken, TokenType: oidc.BearerToken, ExpiresIn: uint64(validity.Seconds()), - Scope: tokenRequest.GetScopes(), } // TODO(v4): remove type assertion - if idTokenRequest, ok := tokenRequest.(IDTokenRequest); ok && slices.Contains(tokenRequest.GetScopes(), oidc.ScopeOpenID) { + if idTokenRequest, ok := tokenRequest.(IDTokenRequest); ok && strs.Contains(tokenRequest.GetScopes(), oidc.ScopeOpenID) { response.IDToken, err = CreateIDToken(ctx, IssuerFromContext(ctx), idTokenRequest, client.IDTokenLifetime(), accessToken, "", creator.Storage(), client) if err != nil { return nil, err diff --git a/pkg/op/device_test.go b/pkg/op/device_test.go index a7b5c4e..570b943 100644 --- a/pkg/op/device_test.go +++ b/pkg/op/device_test.go @@ -13,12 +13,12 @@ import ( "testing" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/storage" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zitadel/oidc/v3/example/server/storage" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) func Test_deviceAuthorizationHandler(t *testing.T) { @@ -145,11 +145,21 @@ func runWithRandReader(r io.Reader, f func()) { } func TestNewDeviceCode(t *testing.T) { - for i := 1; i <= 32; i++ { - got, err := op.NewDeviceCode(i) - require.NoError(t, err) - assert.Len(t, got, base64.RawURLEncoding.EncodedLen(i)) - } + t.Run("reader error", func(t *testing.T) { + runWithRandReader(errReader{}, func() { + _, err := op.NewDeviceCode(16) + require.Error(t, err) + }) + }) + + t.Run("different lengths, rand reader", func(t *testing.T) { + for i := 1; i <= 32; i++ { + got, err := op.NewDeviceCode(i) + require.NoError(t, err) + assert.Len(t, got, base64.RawURLEncoding.EncodedLen(i)) + } + }) + } func TestNewUserCode(t *testing.T) { diff --git a/pkg/op/discovery.go b/pkg/op/discovery.go index 9b3ddb6..cd08580 100644 --- a/pkg/op/discovery.go +++ b/pkg/op/discovery.go @@ -6,8 +6,8 @@ import ( jose "github.com/go-jose/go-jose/v4" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type DiscoverStorage interface { @@ -45,7 +45,6 @@ func CreateDiscoveryConfig(ctx context.Context, config Configuration, storage Di EndSessionEndpoint: config.EndSessionEndpoint().Absolute(issuer), JwksURI: config.KeysEndpoint().Absolute(issuer), DeviceAuthorizationEndpoint: config.DeviceAuthorizationEndpoint().Absolute(issuer), - CheckSessionIframe: config.CheckSessionIframe().Absolute(issuer), ScopesSupported: Scopes(config), ResponseTypesSupported: ResponseTypes(config), GrantTypesSupported: GrantTypes(config), @@ -62,8 +61,6 @@ func CreateDiscoveryConfig(ctx context.Context, config Configuration, storage Di CodeChallengeMethodsSupported: CodeChallengeMethods(config), UILocalesSupported: config.SupportedUILocales(), RequestParameterSupported: config.RequestObjectSupported(), - BackChannelLogoutSupported: config.BackChannelLogoutSupported(), - BackChannelLogoutSessionSupported: config.BackChannelLogoutSessionSupported(), } } @@ -95,17 +92,11 @@ func createDiscoveryConfigV2(ctx context.Context, config Configuration, storage CodeChallengeMethodsSupported: CodeChallengeMethods(config), UILocalesSupported: config.SupportedUILocales(), RequestParameterSupported: config.RequestObjectSupported(), - BackChannelLogoutSupported: config.BackChannelLogoutSupported(), - BackChannelLogoutSessionSupported: config.BackChannelLogoutSessionSupported(), } } func Scopes(c Configuration) []string { - provider, ok := c.(*Provider) - if ok && provider.config.SupportedScopes != nil { - return provider.config.SupportedScopes - } - return DefaultSupportedScopes + return DefaultSupportedScopes // TODO: config } func ResponseTypes(c Configuration) []string { @@ -140,7 +131,7 @@ func GrantTypes(c Configuration) []oidc.GrantType { } func SubjectTypes(c Configuration) []string { - return []string{"public"} // TODO: config + return []string{"public"} //TODO: config } func SigAlgorithms(ctx context.Context, storage DiscoverStorage) []string { diff --git a/pkg/op/discovery_test.go b/pkg/op/discovery_test.go index 63f1b98..cb4cfba 100644 --- a/pkg/op/discovery_test.go +++ b/pkg/op/discovery_test.go @@ -11,9 +11,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op/mock" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op/mock" ) func TestDiscover(t *testing.T) { @@ -81,11 +81,6 @@ func Test_scopes(t *testing.T) { args{}, op.DefaultSupportedScopes, }, - { - "custom scopes", - args{newTestProvider(&op.Config{SupportedScopes: []string{"test1", "test2"}})}, - []string{"test1", "test2"}, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/op/endpoint_test.go b/pkg/op/endpoint_test.go index 5b98c6e..bf112ef 100644 --- a/pkg/op/endpoint_test.go +++ b/pkg/op/endpoint_test.go @@ -3,8 +3,8 @@ package op_test import ( "testing" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" "github.com/stretchr/testify/require" + "github.com/zitadel/oidc/v3/pkg/op" ) func TestEndpoint_Path(t *testing.T) { diff --git a/pkg/op/error.go b/pkg/op/error.go index 272f85e..44b1798 100644 --- a/pkg/op/error.go +++ b/pkg/op/error.go @@ -7,8 +7,8 @@ import ( "log/slog" "net/http" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type ErrAuthRequest interface { @@ -46,12 +46,6 @@ func AuthRequestError(w http.ResponseWriter, r *http.Request, authReq ErrAuthReq return } e.State = authReq.GetState() - var sessionState string - authRequestSessionState, ok := authReq.(AuthRequestSessionState) - if ok { - sessionState = authRequestSessionState.GetSessionState() - } - e.SessionState = sessionState var responseMode oidc.ResponseMode if rm, ok := authReq.(interface{ GetResponseMode() oidc.ResponseMode }); ok { responseMode = rm.GetResponseMode() @@ -98,12 +92,6 @@ func TryErrorRedirect(ctx context.Context, authReq ErrAuthRequest, parent error, } e.State = authReq.GetState() - var sessionState string - authRequestSessionState, ok := authReq.(AuthRequestSessionState) - if ok { - sessionState = authRequestSessionState.GetSessionState() - } - e.SessionState = sessionState var responseMode oidc.ResponseMode if rm, ok := authReq.(interface{ GetResponseMode() oidc.ResponseMode }); ok { responseMode = rm.GetResponseMode() diff --git a/pkg/op/error_test.go b/pkg/op/error_test.go index 9271cf1..170039c 100644 --- a/pkg/op/error_test.go +++ b/pkg/op/error_test.go @@ -11,9 +11,9 @@ import ( "strings" "testing" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/schema" ) @@ -428,8 +428,7 @@ func TestTryErrorRedirect(t *testing.T) { parent: oidc.ErrInteractionRequired().WithDescription("sign in"), }, want: &Redirect{ - Header: make(http.Header), - URL: "http://example.com/callback?error=interaction_required&error_description=sign+in&state=state1", + URL: "http://example.com/callback?error=interaction_required&error_description=sign+in&state=state1", }, wantLog: `{ "level":"WARN", diff --git a/pkg/op/keys.go b/pkg/op/keys.go index 97e400b..c96c456 100644 --- a/pkg/op/keys.go +++ b/pkg/op/keys.go @@ -6,7 +6,7 @@ import ( jose "github.com/go-jose/go-jose/v4" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" + httphelper "github.com/zitadel/oidc/v3/pkg/http" ) type KeyProvider interface { diff --git a/pkg/op/keys_test.go b/pkg/op/keys_test.go index 9c80878..3662739 100644 --- a/pkg/op/keys_test.go +++ b/pkg/op/keys_test.go @@ -11,9 +11,9 @@ import ( "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op/mock" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/op/mock" ) func TestKeys(t *testing.T) { diff --git a/pkg/op/mock/authorizer.mock.go b/pkg/op/mock/authorizer.mock.go index 56b28e0..c7703f1 100644 --- a/pkg/op/mock/authorizer.mock.go +++ b/pkg/op/mock/authorizer.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: Authorizer) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: Authorizer) // Package mock is a generated GoMock package. package mock @@ -9,9 +9,9 @@ import ( slog "log/slog" reflect "reflect" - http "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - op "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" gomock "github.com/golang/mock/gomock" + http "github.com/zitadel/oidc/v3/pkg/http" + op "github.com/zitadel/oidc/v3/pkg/op" ) // MockAuthorizer is a mock of Authorizer interface. diff --git a/pkg/op/mock/authorizer.mock.impl.go b/pkg/op/mock/authorizer.mock.impl.go index 73c4154..59e8fa3 100644 --- a/pkg/op/mock/authorizer.mock.impl.go +++ b/pkg/op/mock/authorizer.mock.impl.go @@ -8,8 +8,8 @@ import ( "github.com/golang/mock/gomock" "github.com/zitadel/schema" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) func NewAuthorizer(t *testing.T) op.Authorizer { diff --git a/pkg/op/mock/client.go b/pkg/op/mock/client.go index e2a5e85..f01e3ec 100644 --- a/pkg/op/mock/client.go +++ b/pkg/op/mock/client.go @@ -5,8 +5,8 @@ import ( "github.com/golang/mock/gomock" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) func NewClient(t *testing.T) op.Client { diff --git a/pkg/op/mock/client.mock.go b/pkg/op/mock/client.mock.go index 93eca67..9be0807 100644 --- a/pkg/op/mock/client.mock.go +++ b/pkg/op/mock/client.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: Client) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: Client) // Package mock is a generated GoMock package. package mock @@ -8,9 +8,9 @@ import ( reflect "reflect" time "time" - oidc "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - op "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" gomock "github.com/golang/mock/gomock" + oidc "github.com/zitadel/oidc/v3/pkg/oidc" + op "github.com/zitadel/oidc/v3/pkg/op" ) // MockClient is a mock of Client interface. diff --git a/pkg/op/mock/configuration.mock.go b/pkg/op/mock/configuration.mock.go index bf51035..f392a45 100644 --- a/pkg/op/mock/configuration.mock.go +++ b/pkg/op/mock/configuration.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: Configuration) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: Configuration) // Package mock is a generated GoMock package. package mock @@ -8,8 +8,8 @@ import ( http "net/http" reflect "reflect" - op "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" gomock "github.com/golang/mock/gomock" + op "github.com/zitadel/oidc/v3/pkg/op" language "golang.org/x/text/language" ) @@ -78,48 +78,6 @@ func (mr *MockConfigurationMockRecorder) AuthorizationEndpoint() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthorizationEndpoint", reflect.TypeOf((*MockConfiguration)(nil).AuthorizationEndpoint)) } -// BackChannelLogoutSessionSupported mocks base method. -func (m *MockConfiguration) BackChannelLogoutSessionSupported() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BackChannelLogoutSessionSupported") - ret0, _ := ret[0].(bool) - return ret0 -} - -// BackChannelLogoutSessionSupported indicates an expected call of BackChannelLogoutSessionSupported. -func (mr *MockConfigurationMockRecorder) BackChannelLogoutSessionSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BackChannelLogoutSessionSupported", reflect.TypeOf((*MockConfiguration)(nil).BackChannelLogoutSessionSupported)) -} - -// BackChannelLogoutSupported mocks base method. -func (m *MockConfiguration) BackChannelLogoutSupported() bool { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "BackChannelLogoutSupported") - ret0, _ := ret[0].(bool) - return ret0 -} - -// BackChannelLogoutSupported indicates an expected call of BackChannelLogoutSupported. -func (mr *MockConfigurationMockRecorder) BackChannelLogoutSupported() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BackChannelLogoutSupported", reflect.TypeOf((*MockConfiguration)(nil).BackChannelLogoutSupported)) -} - -// CheckSessionIframe mocks base method. -func (m *MockConfiguration) CheckSessionIframe() *op.Endpoint { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CheckSessionIframe") - ret0, _ := ret[0].(*op.Endpoint) - return ret0 -} - -// CheckSessionIframe indicates an expected call of CheckSessionIframe. -func (mr *MockConfigurationMockRecorder) CheckSessionIframe() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckSessionIframe", reflect.TypeOf((*MockConfiguration)(nil).CheckSessionIframe)) -} - // CodeMethodS256Supported mocks base method. func (m *MockConfiguration) CodeMethodS256Supported() bool { m.ctrl.T.Helper() diff --git a/pkg/op/mock/discovery.mock.go b/pkg/op/mock/discovery.mock.go index c85f91b..a27f8ef 100644 --- a/pkg/op/mock/discovery.mock.go +++ b/pkg/op/mock/discovery.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: DiscoverStorage) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: DiscoverStorage) // Package mock is a generated GoMock package. package mock diff --git a/pkg/op/mock/generate.go b/pkg/op/mock/generate.go index 3d58ab7..e5cab3e 100644 --- a/pkg/op/mock/generate.go +++ b/pkg/op/mock/generate.go @@ -1,11 +1,11 @@ package mock //go:generate go install github.com/golang/mock/mockgen@v1.6.0 -//go:generate mockgen -package mock -destination ./storage.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op Storage -//go:generate mockgen -package mock -destination ./authorizer.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op Authorizer -//go:generate mockgen -package mock -destination ./client.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op Client -//go:generate mockgen -package mock -destination ./glob.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op HasRedirectGlobs -//go:generate mockgen -package mock -destination ./configuration.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op Configuration -//go:generate mockgen -package mock -destination ./discovery.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op DiscoverStorage -//go:generate mockgen -package mock -destination ./signer.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op SigningKey,Key -//go:generate mockgen -package mock -destination ./key.mock.go git.christmann.info/LARA/zitadel-oidc/v3/pkg/op KeyProvider +//go:generate mockgen -package mock -destination ./storage.mock.go github.com/zitadel/oidc/v3/pkg/op Storage +//go:generate mockgen -package mock -destination ./authorizer.mock.go github.com/zitadel/oidc/v3/pkg/op Authorizer +//go:generate mockgen -package mock -destination ./client.mock.go github.com/zitadel/oidc/v3/pkg/op Client +//go:generate mockgen -package mock -destination ./glob.mock.go github.com/zitadel/oidc/v3/pkg/op HasRedirectGlobs +//go:generate mockgen -package mock -destination ./configuration.mock.go github.com/zitadel/oidc/v3/pkg/op Configuration +//go:generate mockgen -package mock -destination ./discovery.mock.go github.com/zitadel/oidc/v3/pkg/op DiscoverStorage +//go:generate mockgen -package mock -destination ./signer.mock.go github.com/zitadel/oidc/v3/pkg/op SigningKey,Key +//go:generate mockgen -package mock -destination ./key.mock.go github.com/zitadel/oidc/v3/pkg/op KeyProvider diff --git a/pkg/op/mock/glob.go b/pkg/op/mock/glob.go index 8149c8f..cade476 100644 --- a/pkg/op/mock/glob.go +++ b/pkg/op/mock/glob.go @@ -3,9 +3,9 @@ package mock import ( "testing" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - op "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" gomock "github.com/golang/mock/gomock" + "github.com/zitadel/oidc/v3/pkg/oidc" + op "github.com/zitadel/oidc/v3/pkg/op" ) func NewHasRedirectGlobs(t *testing.T) op.HasRedirectGlobs { diff --git a/pkg/op/mock/glob.mock.go b/pkg/op/mock/glob.mock.go index ebdc333..cf9996e 100644 --- a/pkg/op/mock/glob.mock.go +++ b/pkg/op/mock/glob.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: HasRedirectGlobs) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: HasRedirectGlobs) // Package mock is a generated GoMock package. package mock @@ -8,9 +8,9 @@ import ( reflect "reflect" time "time" - oidc "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - op "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" gomock "github.com/golang/mock/gomock" + oidc "github.com/zitadel/oidc/v3/pkg/oidc" + op "github.com/zitadel/oidc/v3/pkg/op" ) // MockHasRedirectGlobs is a mock of HasRedirectGlobs interface. diff --git a/pkg/op/mock/key.mock.go b/pkg/op/mock/key.mock.go index d9ee857..122e852 100644 --- a/pkg/op/mock/key.mock.go +++ b/pkg/op/mock/key.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: KeyProvider) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: KeyProvider) // Package mock is a generated GoMock package. package mock @@ -8,8 +8,8 @@ import ( context "context" reflect "reflect" - op "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" gomock "github.com/golang/mock/gomock" + op "github.com/zitadel/oidc/v3/pkg/op" ) // MockKeyProvider is a mock of KeyProvider interface. diff --git a/pkg/op/mock/signer.mock.go b/pkg/op/mock/signer.mock.go index 751ce60..e1bab91 100644 --- a/pkg/op/mock/signer.mock.go +++ b/pkg/op/mock/signer.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: SigningKey,Key) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: SigningKey,Key) // Package mock is a generated GoMock package. package mock diff --git a/pkg/op/mock/storage.mock.go b/pkg/op/mock/storage.mock.go index 0df9830..02a7c5c 100644 --- a/pkg/op/mock/storage.mock.go +++ b/pkg/op/mock/storage.mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: git.christmann.info/LARA/zitadel-oidc/v3/pkg/op (interfaces: Storage) +// Source: github.com/zitadel/oidc/v3/pkg/op (interfaces: Storage) // Package mock is a generated GoMock package. package mock @@ -9,10 +9,10 @@ import ( reflect "reflect" time "time" - oidc "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - op "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" jose "github.com/go-jose/go-jose/v4" gomock "github.com/golang/mock/gomock" + oidc "github.com/zitadel/oidc/v3/pkg/oidc" + op "github.com/zitadel/oidc/v3/pkg/op" ) // MockStorage is a mock of Storage interface. diff --git a/pkg/op/mock/storage.mock.impl.go b/pkg/op/mock/storage.mock.impl.go index 96e08a9..002da7e 100644 --- a/pkg/op/mock/storage.mock.impl.go +++ b/pkg/op/mock/storage.mock.impl.go @@ -8,8 +8,8 @@ import ( "github.com/golang/mock/gomock" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) func NewStorage(t *testing.T) op.Storage { diff --git a/pkg/op/op.go b/pkg/op/op.go index 76c2c89..61c2449 100644 --- a/pkg/op/op.go +++ b/pkg/op/op.go @@ -14,8 +14,8 @@ import ( "go.opentelemetry.io/otel" "golang.org/x/text/language" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) const ( @@ -158,19 +158,16 @@ func authCallbackPath(o OpenIDProvider) string { } type Config struct { - CryptoKey [32]byte - DefaultLogoutRedirectURI string - CodeMethodS256 bool - AuthMethodPost bool - AuthMethodPrivateKeyJWT bool - GrantTypeRefreshToken bool - RequestObjectSupported bool - SupportedUILocales []language.Tag - SupportedClaims []string - SupportedScopes []string - DeviceAuthorization DeviceAuthorizationConfig - BackChannelLogoutSupported bool - BackChannelLogoutSessionSupported bool + CryptoKey [32]byte + DefaultLogoutRedirectURI string + CodeMethodS256 bool + AuthMethodPost bool + AuthMethodPrivateKeyJWT bool + GrantTypeRefreshToken bool + RequestObjectSupported bool + SupportedUILocales []language.Tag + SupportedClaims []string + DeviceAuthorization DeviceAuthorizationConfig } // Endpoints defines endpoint routes. @@ -339,10 +336,6 @@ func (o *Provider) DeviceAuthorizationEndpoint() *Endpoint { return o.endpoints.DeviceAuthorization } -func (o *Provider) CheckSessionIframe() *Endpoint { - return o.endpoints.CheckSessionIframe -} - func (o *Provider) KeysEndpoint() *Endpoint { return o.endpoints.JwksURI } @@ -418,14 +411,6 @@ func (o *Provider) DeviceAuthorization() DeviceAuthorizationConfig { return o.config.DeviceAuthorization } -func (o *Provider) BackChannelLogoutSupported() bool { - return o.config.BackChannelLogoutSupported -} - -func (o *Provider) BackChannelLogoutSessionSupported() bool { - return o.config.BackChannelLogoutSessionSupported -} - func (o *Provider) Storage() Storage { return o.storage } diff --git a/pkg/op/op_test.go b/pkg/op/op_test.go index e1ac0bd..83032d4 100644 --- a/pkg/op/op_test.go +++ b/pkg/op/op_test.go @@ -11,12 +11,12 @@ import ( "testing" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/example/server/storage" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zitadel/oidc/v3/example/server/storage" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" "golang.org/x/text/language" ) @@ -232,7 +232,7 @@ func TestRoutes(t *testing.T) { "scope": oidc.SpaceDelimitedArray{oidc.ScopeOpenID, oidc.ScopeOfflineAccess}.String(), }, wantCode: http.StatusOK, - contains: []string{`{"access_token":"`, `","token_type":"Bearer","expires_in":299,"scope":"openid offline_access"}`}, + contains: []string{`{"access_token":"`, `","token_type":"Bearer","expires_in":299}`}, }, { // This call will fail. A successful test is already diff --git a/pkg/op/probes.go b/pkg/op/probes.go index fa713da..cb3853d 100644 --- a/pkg/op/probes.go +++ b/pkg/op/probes.go @@ -5,7 +5,7 @@ import ( "errors" "net/http" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" + httphelper "github.com/zitadel/oidc/v3/pkg/http" ) type ProbesFn func(context.Context) error diff --git a/pkg/op/server.go b/pkg/op/server.go index d45b734..6faee87 100644 --- a/pkg/op/server.go +++ b/pkg/op/server.go @@ -5,9 +5,9 @@ import ( "net/http" "net/url" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/muhlemmer/gu" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // Server describes the interface that needs to be implemented to serve @@ -218,8 +218,7 @@ type Response struct { // without custom headers. func NewResponse(data any) *Response { return &Response{ - Header: make(http.Header), - Data: data, + Data: data, } } @@ -243,10 +242,7 @@ type Redirect struct { } func NewRedirect(url string) *Redirect { - return &Redirect{ - Header: make(http.Header), - URL: url, - } + return &Redirect{URL: url} } func (red *Redirect) writeOut(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/op/server_http.go b/pkg/op/server_http.go index d71a354..725dd64 100644 --- a/pkg/op/server_http.go +++ b/pkg/op/server_http.go @@ -6,11 +6,11 @@ import ( "net/http" "net/url" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/go-chi/chi/v5" "github.com/rs/cors" "github.com/zitadel/logging" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/schema" ) diff --git a/pkg/op/server_http_routes_test.go b/pkg/op/server_http_routes_test.go index 02200ee..2c83ad3 100644 --- a/pkg/op/server_http_routes_test.go +++ b/pkg/op/server_http_routes_test.go @@ -14,9 +14,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/client" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + "github.com/zitadel/oidc/v3/pkg/client" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) func jwtProfile() (string, error) { @@ -145,7 +145,7 @@ func TestServerRoutes(t *testing.T) { "assertion": jwtProfileToken, }, wantCode: http.StatusOK, - contains: []string{`{"access_token":`, `"token_type":"Bearer","expires_in":299,"scope":"openid"}`}, + contains: []string{`{"access_token":`, `"token_type":"Bearer","expires_in":299}`}, }, { name: "Token exchange", @@ -174,7 +174,7 @@ func TestServerRoutes(t *testing.T) { "scope": oidc.SpaceDelimitedArray{oidc.ScopeOpenID, oidc.ScopeOfflineAccess}.String(), }, wantCode: http.StatusOK, - contains: []string{`{"access_token":"`, `","token_type":"Bearer","expires_in":299,"scope":"openid offline_access"}`}, + contains: []string{`{"access_token":"`, `","token_type":"Bearer","expires_in":299}`}, }, { // This call will fail. A successful test is already diff --git a/pkg/op/server_http_test.go b/pkg/op/server_http_test.go index 75d02ca..9ff07bc 100644 --- a/pkg/op/server_http_test.go +++ b/pkg/op/server_http_test.go @@ -14,11 +14,11 @@ import ( "testing" "time" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/schema" ) diff --git a/pkg/op/server_legacy.go b/pkg/op/server_legacy.go index 06e4e93..126fde1 100644 --- a/pkg/op/server_legacy.go +++ b/pkg/op/server_legacy.go @@ -6,8 +6,8 @@ import ( "net/http" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/go-chi/chi/v5" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // ExtendedLegacyServer allows embedding [LegacyServer] in a struct, diff --git a/pkg/op/session.go b/pkg/op/session.go index ac663c9..8ac530d 100644 --- a/pkg/op/session.go +++ b/pkg/op/session.go @@ -8,8 +8,8 @@ import ( "net/url" "path" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type SessionEnder interface { @@ -73,8 +73,6 @@ func ValidateEndSessionRequest(ctx context.Context, req *oidc.EndSessionRequest, session := &EndSessionRequest{ RedirectURI: ender.DefaultLogoutRedirectURI(), - LogoutHint: req.LogoutHint, - UILocales: req.UILocales, } if req.IdTokenHint != "" { claims, err := VerifyIDTokenHint[*oidc.IDTokenClaims](ctx, req.IdTokenHint, ender.IDTokenHintVerifier(ctx)) diff --git a/pkg/op/storage.go b/pkg/op/storage.go index 2dbd124..8488b28 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -6,9 +6,8 @@ import ( "time" jose "github.com/go-jose/go-jose/v4" - "golang.org/x/text/language" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type AuthStorage interface { @@ -145,12 +144,6 @@ type CanSetUserinfoFromRequest interface { SetUserinfoFromRequest(ctx context.Context, userinfo *oidc.UserInfo, request IDTokenRequest, scopes []string) error } -// CanGetPrivateClaimsFromRequest is an optional additional interface that may be implemented by -// implementors of Storage. It allows setting the jwt token claims based on the request. -type CanGetPrivateClaimsFromRequest interface { - GetPrivateClaimsFromRequest(ctx context.Context, request TokenRequest, restrictedScopes []string) (map[string]any, error) -} - // Storage is a required parameter for NewOpenIDProvider(). In addition to the // embedded interfaces below, if the passed Storage implements ClientCredentialsStorage // then the grant type "client_credentials" will be supported. In that case, the access @@ -171,8 +164,6 @@ type EndSessionRequest struct { ClientID string IDTokenHintClaims *oidc.IDTokenClaims RedirectURI string - LogoutHint string - UILocales []language.Tag } var ErrDuplicateUserCode = errors.New("user code already exists") diff --git a/pkg/op/token.go b/pkg/op/token.go index 2e25d05..b45789b 100644 --- a/pkg/op/token.go +++ b/pkg/op/token.go @@ -2,11 +2,11 @@ package op import ( "context" - "slices" "time" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/crypto" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/crypto" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/strings" ) type TokenCreator interface { @@ -65,7 +65,6 @@ func CreateTokenResponse(ctx context.Context, request IDTokenRequest, client Cli TokenType: oidc.BearerToken, ExpiresIn: exp, State: state, - Scope: request.GetScopes(), }, nil } @@ -83,13 +82,13 @@ func createTokens(ctx context.Context, tokenRequest TokenRequest, storage Storag func needsRefreshToken(tokenRequest TokenRequest, client AccessTokenClient) bool { switch req := tokenRequest.(type) { case AuthRequest: - return slices.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && req.GetResponseType() == oidc.ResponseTypeCode && ValidateGrantType(client, oidc.GrantTypeRefreshToken) + return strings.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && req.GetResponseType() == oidc.ResponseTypeCode && ValidateGrantType(client, oidc.GrantTypeRefreshToken) case TokenExchangeRequest: return req.GetRequestedTokenType() == oidc.RefreshTokenType case RefreshTokenRequest: return true case *DeviceAuthorizationState: - return slices.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && ValidateGrantType(client, oidc.GrantTypeRefreshToken) + return strings.Contains(req.GetScopes(), oidc.ScopeOfflineAccess) && ValidateGrantType(client, oidc.GrantTypeRefreshToken) default: return false } @@ -147,11 +146,7 @@ func CreateJWT(ctx context.Context, issuer string, tokenRequest TokenRequest, ex tokenExchangeRequest, ) } else { - if fromRequest, ok := storage.(CanGetPrivateClaimsFromRequest); ok { - privateClaims, err = fromRequest.GetPrivateClaimsFromRequest(ctx, tokenRequest, removeUserinfoScopes(restrictedScopes)) - } else { - privateClaims, err = storage.GetPrivateClaimsFromScopes(ctx, tokenRequest.GetSubject(), client.GetID(), removeUserinfoScopes(restrictedScopes)) - } + privateClaims, err = storage.GetPrivateClaimsFromScopes(ctx, tokenRequest.GetSubject(), client.GetID(), removeUserinfoScopes(restrictedScopes)) } if err != nil { diff --git a/pkg/op/token_client_credentials.go b/pkg/op/token_client_credentials.go index ddb2fbf..7f1debe 100644 --- a/pkg/op/token_client_credentials.go +++ b/pkg/op/token_client_credentials.go @@ -5,8 +5,8 @@ import ( "net/http" "net/url" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // ClientCredentialsExchange handles the OAuth 2.0 client_credentials grant, including @@ -120,6 +120,5 @@ func CreateClientCredentialsTokenResponse(ctx context.Context, tokenRequest Toke AccessToken: accessToken, TokenType: oidc.BearerToken, ExpiresIn: uint64(validity.Seconds()), - Scope: tokenRequest.GetScopes(), }, nil } diff --git a/pkg/op/token_code.go b/pkg/op/token_code.go index 155aa43..3612240 100644 --- a/pkg/op/token_code.go +++ b/pkg/op/token_code.go @@ -4,8 +4,8 @@ import ( "context" "net/http" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // CodeExchange handles the OAuth 2.0 authorization_code grant, including diff --git a/pkg/op/token_exchange.go b/pkg/op/token_exchange.go index 00af485..fcb4468 100644 --- a/pkg/op/token_exchange.go +++ b/pkg/op/token_exchange.go @@ -7,8 +7,8 @@ import ( "strings" "time" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type TokenExchangeRequest interface { diff --git a/pkg/op/token_intospection.go b/pkg/op/token_intospection.go index bb6a5a0..29234e1 100644 --- a/pkg/op/token_intospection.go +++ b/pkg/op/token_intospection.go @@ -5,8 +5,8 @@ import ( "errors" "net/http" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type Introspector interface { diff --git a/pkg/op/token_jwt_profile.go b/pkg/op/token_jwt_profile.go index defb937..96ce1ed 100644 --- a/pkg/op/token_jwt_profile.go +++ b/pkg/op/token_jwt_profile.go @@ -5,8 +5,8 @@ import ( "net/http" "time" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type JWTAuthorizationGrantExchanger interface { @@ -89,7 +89,6 @@ func CreateJWTTokenResponse(ctx context.Context, tokenRequest TokenRequest, crea AccessToken: accessToken, TokenType: oidc.BearerToken, ExpiresIn: uint64(validity.Seconds()), - Scope: tokenRequest.GetScopes(), }, nil } diff --git a/pkg/op/token_refresh.go b/pkg/op/token_refresh.go index a87e883..92ef476 100644 --- a/pkg/op/token_refresh.go +++ b/pkg/op/token_refresh.go @@ -4,11 +4,11 @@ import ( "context" "errors" "net/http" - "slices" "time" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/strings" ) type RefreshTokenRequest interface { @@ -85,7 +85,7 @@ func ValidateRefreshTokenScopes(requestedScopes []string, authRequest RefreshTok return nil } for _, scope := range requestedScopes { - if !slices.Contains(authRequest.GetScopes(), scope) { + if !strings.Contains(authRequest.GetScopes(), scope) { return oidc.ErrInvalidScope() } } diff --git a/pkg/op/token_request.go b/pkg/op/token_request.go index 3f5af7a..85e2270 100644 --- a/pkg/op/token_request.go +++ b/pkg/op/token_request.go @@ -6,8 +6,8 @@ import ( "net/http" "net/url" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type Exchanger interface { @@ -132,19 +132,11 @@ func AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string, // AuthorizeCodeChallenge authorizes a client by validating the code_verifier against the previously sent // code_challenge of the auth request (PKCE) func AuthorizeCodeChallenge(codeVerifier string, challenge *oidc.CodeChallenge) error { - if challenge == nil { - if codeVerifier != "" { - return oidc.ErrInvalidRequest().WithDescription("code_verifier unexpectedly provided") - } - - return nil - } - if codeVerifier == "" { - return oidc.ErrInvalidRequest().WithDescription("code_verifier required") + return oidc.ErrInvalidRequest().WithDescription("code_challenge required") } if !oidc.VerifyCodeChallenge(challenge, codeVerifier) { - return oidc.ErrInvalidGrant().WithDescription("invalid code_verifier") + return oidc.ErrInvalidGrant().WithDescription("invalid code challenge") } return nil } diff --git a/pkg/op/token_request_test.go b/pkg/op/token_request_test.go deleted file mode 100644 index d226af6..0000000 --- a/pkg/op/token_request_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package op_test - -import ( - "testing" - - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" - "github.com/stretchr/testify/assert" -) - -func TestAuthorizeCodeChallenge(t *testing.T) { - tests := []struct { - name string - codeVerifier string - codeChallenge *oidc.CodeChallenge - want func(t *testing.T, err error) - }{ - { - name: "missing both code_verifier and code_challenge", - codeVerifier: "", - codeChallenge: nil, - want: func(t *testing.T, err error) { - assert.Nil(t, err) - }, - }, - { - name: "valid code_verifier", - codeVerifier: "Hello World!", - codeChallenge: &oidc.CodeChallenge{ - Challenge: "f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", - Method: oidc.CodeChallengeMethodS256, - }, - want: func(t *testing.T, err error) { - assert.Nil(t, err) - }, - }, - { - name: "invalid code_verifier", - codeVerifier: "Hi World!", - codeChallenge: &oidc.CodeChallenge{ - Challenge: "f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", - Method: oidc.CodeChallengeMethodS256, - }, - want: func(t *testing.T, err error) { - assert.ErrorContains(t, err, "invalid code_verifier") - }, - }, - { - name: "code_verifier provided without code_challenge", - codeVerifier: "code_verifier", - codeChallenge: nil, - want: func(t *testing.T, err error) { - assert.ErrorContains(t, err, "code_verifier unexpectedly provided") - }, - }, - { - name: "empty code_verifier", - codeVerifier: "", - codeChallenge: &oidc.CodeChallenge{ - Challenge: "f4OxZX_x_FO5LcGBSKHWXfwtSx-j1ncoSt3SABJtkGk", - Method: oidc.CodeChallengeMethodS256, - }, - want: func(t *testing.T, err error) { - assert.ErrorContains(t, err, "code_verifier required") - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - err := op.AuthorizeCodeChallenge(tt.codeVerifier, tt.codeChallenge) - - tt.want(t, err) - }) - } -} diff --git a/pkg/op/token_revocation.go b/pkg/op/token_revocation.go index 049ee15..a86a481 100644 --- a/pkg/op/token_revocation.go +++ b/pkg/op/token_revocation.go @@ -7,8 +7,8 @@ import ( "net/url" "strings" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type Revoker interface { diff --git a/pkg/op/userinfo.go b/pkg/op/userinfo.go index ff75e72..839b139 100644 --- a/pkg/op/userinfo.go +++ b/pkg/op/userinfo.go @@ -6,8 +6,8 @@ import ( "net/http" "strings" - httphelper "git.christmann.info/LARA/zitadel-oidc/v3/pkg/http" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + httphelper "github.com/zitadel/oidc/v3/pkg/http" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type UserinfoProvider interface { diff --git a/pkg/op/verifier_access_token.go b/pkg/op/verifier_access_token.go index 585ca54..6ac29f2 100644 --- a/pkg/op/verifier_access_token.go +++ b/pkg/op/verifier_access_token.go @@ -3,7 +3,7 @@ package op import ( "context" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type AccessTokenVerifier oidc.Verifier diff --git a/pkg/op/verifier_access_token_example_test.go b/pkg/op/verifier_access_token_example_test.go index b97a7fd..397a2d3 100644 --- a/pkg/op/verifier_access_token_example_test.go +++ b/pkg/op/verifier_access_token_example_test.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) // MyCustomClaims extends the TokenClaims base, diff --git a/pkg/op/verifier_access_token_test.go b/pkg/op/verifier_access_token_test.go index 5845f9f..66e32ce 100644 --- a/pkg/op/verifier_access_token_test.go +++ b/pkg/op/verifier_access_token_test.go @@ -5,10 +5,10 @@ import ( "testing" "time" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" ) func TestNewAccessTokenVerifier(t *testing.T) { diff --git a/pkg/op/verifier_id_token_hint.go b/pkg/op/verifier_id_token_hint.go index 02610aa..331c64c 100644 --- a/pkg/op/verifier_id_token_hint.go +++ b/pkg/op/verifier_id_token_hint.go @@ -4,7 +4,7 @@ import ( "context" "errors" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" ) type IDTokenHintVerifier oidc.Verifier diff --git a/pkg/op/verifier_id_token_hint_test.go b/pkg/op/verifier_id_token_hint_test.go index 347e33c..597e291 100644 --- a/pkg/op/verifier_id_token_hint_test.go +++ b/pkg/op/verifier_id_token_hint_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" ) func TestNewIDTokenHintVerifier(t *testing.T) { diff --git a/pkg/op/verifier_jwt_profile.go b/pkg/op/verifier_jwt_profile.go index 85bfb14..06a7d34 100644 --- a/pkg/op/verifier_jwt_profile.go +++ b/pkg/op/verifier_jwt_profile.go @@ -8,7 +8,7 @@ import ( jose "github.com/go-jose/go-jose/v4" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/oidc" ) // JWTProfileVerfiier extends oidc.Verifier with diff --git a/pkg/op/verifier_jwt_profile_test.go b/pkg/op/verifier_jwt_profile_test.go index 2068678..d96cbb4 100644 --- a/pkg/op/verifier_jwt_profile_test.go +++ b/pkg/op/verifier_jwt_profile_test.go @@ -5,11 +5,11 @@ import ( "testing" "time" - tu "git.christmann.info/LARA/zitadel-oidc/v3/internal/testutil" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/oidc" - "git.christmann.info/LARA/zitadel-oidc/v3/pkg/op" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tu "github.com/zitadel/oidc/v3/internal/testutil" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" ) func TestNewJWTProfileVerifier(t *testing.T) { diff --git a/pkg/strings/strings.go b/pkg/strings/strings.go index b8f43a1..af48cf3 100644 --- a/pkg/strings/strings.go +++ b/pkg/strings/strings.go @@ -1,9 +1,10 @@ package strings -import "slices" - -// Deprecated: Use standard library [slices.Contains] instead. func Contains(list []string, needle string) bool { - // TODO(v4): remove package. - return slices.Contains(list, needle) + for _, item := range list { + if item == needle { + return true + } + } + return false }