Compare commits

..

12 commits

Author SHA1 Message Date
Tim Möhlmann
214a899ddd Merge branch 'feat-EdDSA-hasher' into webkey-test-branch 2024-08-20 16:07:59 +03:00
Tim Möhlmann
07d4268b89 example: signing algs from discovery 2024-08-20 16:07:20 +03:00
Tim Möhlmann
0df1caff1b Revert "chore(example): add supported signing algorithms to RP"
This reverts commit eb249c4c70.
2024-08-20 16:03:44 +03:00
Tim Möhlmann
6fd74f21d3 Merge branch 'feat-EdDSA-hasher' into work 2024-08-20 11:47:53 +03:00
Tim Möhlmann
248df8c1f1 rp: modify keytype check to support EdDSA 2024-08-20 11:44:09 +03:00
Tim Möhlmann
0e6aafa16c Merge branch 'chore-example-algs' into work 2024-08-20 11:15:33 +03:00
Tim Möhlmann
f5cd665097 Merge branch 'fix-header-map' into work 2024-08-20 10:47:19 +03:00
Tim Möhlmann
2a3e87afff update code comment 2024-08-20 10:44:45 +03:00
Tim Möhlmann
eb249c4c70 chore(example): add supported signing algorithms to RP 2024-08-19 21:04:21 +03:00
Tim Möhlmann
7e1846e6e2 feat(crypto): hash algorithm for EdDSA 2024-08-19 20:57:07 +03:00
Tim Möhlmann
fa73f36780 fix test 2024-08-18 13:14:01 +03:00
Tim Möhlmann
f2545780c8 fix(op): initialize http Headers in response objects 2024-08-17 16:35:30 +03:00
126 changed files with 538 additions and 1550 deletions

View file

@ -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:

View file

@ -1,7 +1,6 @@
name: 📄 Documentation
description: Create an issue for missing or wrong documentation.
labels: ["docs"]
type: task
body:
- type: markdown
attributes:

View file

@ -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:

44
.github/ISSUE_TEMPLATE/proposal.yaml vendored Normal file
View file

@ -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.

View file

@ -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:

View file

@ -21,7 +21,6 @@ Whenever possible we tried to reuse / extend existing packages like `OAuth2 for
## Basic Overview
The most important packages of the library:
<pre>
/pkg
/client clients using the OP for retrieving, exchanging and verifying tokens
@ -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
</pre>
### 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.
@ -60,54 +60,20 @@ CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://localhost:9998/ SCOPES="openid
- 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] |
@ -119,20 +85,18 @@ Here is json equivalent for one of the default users
| 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 |
[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]: <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"
## Contributors
@ -156,9 +120,9 @@ 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

View file

@ -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 (

View file

@ -14,10 +14,10 @@ import (
"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 (

View file

@ -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 (

View file

@ -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 (

View file

@ -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

View file

@ -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
}

View file

@ -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)
}
})
}
}

View file

@ -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 (

View file

@ -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 (

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View file

@ -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)
}

View file

@ -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 (

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -22,5 +22,4 @@ type RefreshToken struct {
ApplicationID string
Expiration time.Time
Scopes []string
AccessToken string // Token.ID
}

View file

@ -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{

View file

@ -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)
}
})
}
}

38
go.mod
View file

@ -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
)

68
go.sum
View file

@ -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=

View file

@ -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{

View file

@ -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

View file

@ -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)

View file

@ -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
}

View file

@ -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(

View file

@ -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

View file

@ -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
}

View file

@ -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 {

View file

@ -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 (

View file

@ -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

View file

@ -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) {

View file

@ -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 {

View file

@ -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

View file

@ -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"
)

View file

@ -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`

View file

@ -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 {

View file

@ -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

View file

@ -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) {

View file

@ -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,

View file

@ -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 {

View file

@ -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 {

View file

@ -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) {

View file

@ -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 {

View file

@ -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) {

View file

@ -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{

View file

@ -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 (

View file

@ -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

View file

@ -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))
}

View file

@ -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"`
}

View file

@ -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,
},
}
@ -236,7 +235,6 @@ type AccessTokenResponse struct {
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"`
}
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,
}
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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
}

View file

@ -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,8 +237,8 @@ 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 {
@ -271,7 +247,6 @@ func TestLocale_UnmarshalJSON(t *testing.T) {
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
})
}
}

View file

@ -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)
}

View file

@ -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) {

View file

@ -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" {
if parsedURL.Scheme != "http" {
return nil, false
}
hostName := parsedURL.Hostname()
return parsedURL, hostName == "localhost" || net.ParseIP(hostName).IsLoopback()
}
return nil, false
}
// 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
}
}
// 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 err != nil {
return err
}
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{
codeResponse := struct {
Code string `schema:"code"`
State string `schema:"state,omitempty"`
}{
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())
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 {
AuthRequestError(w, r, authReq, err, authorizer)
return
}
http.Redirect(w, r, callback, http.StatusFound)
}
// AuthResponseToken creates the successful token(s) authentication response

View file

@ -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)
}
}
})
}
}

View file

@ -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

View file

@ -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"
)

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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) {
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) {

View file

@ -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 {

View file

@ -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) {

View file

@ -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) {

View file

@ -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()

View file

@ -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"
)

View file

@ -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 {

View file

@ -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) {

View file

@ -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.

View file

@ -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 {

View file

@ -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 {

View file

@ -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.

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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 {

View file

@ -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 (
@ -167,10 +167,7 @@ type Config struct {
RequestObjectSupported bool
SupportedUILocales []language.Tag
SupportedClaims []string
SupportedScopes []string
DeviceAuthorization DeviceAuthorizationConfig
BackChannelLogoutSupported bool
BackChannelLogoutSessionSupported bool
}
// 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
}

View file

@ -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

View file

@ -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

Some files were not shown because too many files have changed in this diff Show more