mirror of
https://github.com/coder/coder.git
synced 2025-07-18 14:17:22 +00:00
feat: implement disabling oidc issuer checks (#13991)
* use DANGEROUS prefix and drop a warning log
This commit is contained in:
@ -2,19 +2,22 @@ package oidctest_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/coder/coder/v2/coderd"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
// TestFakeIDPBasicFlow tests the basic flow of the fake IDP.
|
||||
@ -27,12 +30,6 @@ func TestFakeIDPBasicFlow(t *testing.T) {
|
||||
oidctest.WithLogging(t, nil),
|
||||
)
|
||||
|
||||
var handler http.Handler
|
||||
srv := httptest.NewServer(http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
handler.ServeHTTP(w, r)
|
||||
})))
|
||||
defer srv.Close()
|
||||
|
||||
cfg := fake.OIDCConfig(t, nil)
|
||||
cli := fake.HTTPClient(nil)
|
||||
ctx := oidc.ClientContext(context.Background(), cli)
|
||||
@ -71,3 +68,84 @@ func TestFakeIDPBasicFlow(t *testing.T) {
|
||||
require.NoError(t, err, "failed to refresh token")
|
||||
require.NotEmpty(t, refreshed.AccessToken, "access token is empty on refresh")
|
||||
}
|
||||
|
||||
// TestIDPIssuerMismatch emulates a situation where the IDP issuer url does
|
||||
// not match the one in the well-known config and claims.
|
||||
// This can happen in some edge cases and in some azure configurations.
|
||||
//
|
||||
// This test just makes sure a fake IDP can set up this scenario.
|
||||
func TestIDPIssuerMismatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const proxyURL = "https://proxy.com"
|
||||
const primaryURL = "https://primary.com"
|
||||
|
||||
fake := oidctest.NewFakeIDP(t,
|
||||
oidctest.WithIssuer(proxyURL),
|
||||
oidctest.WithDefaultIDClaims(jwt.MapClaims{
|
||||
"iss": primaryURL,
|
||||
}),
|
||||
oidctest.WithHookWellKnown(func(r *http.Request, j *oidctest.ProviderJSON) error {
|
||||
// host should be proxy.com, but we return the primaryURL
|
||||
if r.Host != "proxy.com" {
|
||||
return xerrors.Errorf("unexpected host: %s", r.Host)
|
||||
}
|
||||
j.Issuer = primaryURL
|
||||
return nil
|
||||
}),
|
||||
oidctest.WithLogging(t, nil),
|
||||
)
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
// Do not use real network requests
|
||||
cli := fake.HTTPClient(nil)
|
||||
ctx = oidc.ClientContext(ctx, cli)
|
||||
|
||||
// Allow the issuer mismatch
|
||||
verifierContext := oidc.InsecureIssuerURLContext(ctx, "this field does not matter")
|
||||
p, err := oidc.NewProvider(verifierContext, "https://proxy.com")
|
||||
require.NoError(t, err, "failed to create OIDC provider")
|
||||
|
||||
oauthConfig := fake.OauthConfig(t, nil)
|
||||
cfg := &coderd.OIDCConfig{
|
||||
OAuth2Config: oauthConfig,
|
||||
Provider: p,
|
||||
Verifier: oidc.NewVerifier(fake.WellknownConfig().Issuer, &oidc.StaticKeySet{
|
||||
PublicKeys: []crypto.PublicKey{fake.PublicKey()},
|
||||
}, &oidc.Config{
|
||||
SkipIssuerCheck: true,
|
||||
ClientID: oauthConfig.ClientID,
|
||||
SupportedSigningAlgs: []string{
|
||||
"RS256",
|
||||
},
|
||||
}),
|
||||
UsernameField: "preferred_username",
|
||||
EmailField: "email",
|
||||
AuthURLParams: map[string]string{"access_type": "offline"},
|
||||
}
|
||||
|
||||
const expectedState = "random-state"
|
||||
var token *oauth2.Token
|
||||
|
||||
fake.SetCoderdCallbackHandler(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Emulate OIDC flow
|
||||
code := r.URL.Query().Get("code")
|
||||
state := r.URL.Query().Get("state")
|
||||
assert.Equal(t, expectedState, state, "state mismatch")
|
||||
|
||||
oauthToken, err := cfg.Exchange(ctx, code)
|
||||
if assert.NoError(t, err, "failed to exchange code") {
|
||||
assert.NotEmpty(t, oauthToken.AccessToken, "access token is empty")
|
||||
assert.NotEmpty(t, oauthToken.RefreshToken, "refresh token is empty")
|
||||
}
|
||||
token = oauthToken
|
||||
})
|
||||
|
||||
//nolint:bodyclose
|
||||
resp := fake.OIDCCallback(t, expectedState, nil) // Use default claims
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
idToken, err := cfg.Verifier.Verify(ctx, token.Extra("id_token").(string))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, primaryURL, idToken.Issuer)
|
||||
}
|
||||
|
Reference in New Issue
Block a user