mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
bddb808b25
Fixes all our Go file imports to match the preferred spec that we've _mostly_ been using. For example: ``` import ( "context" "time" "github.com/prometheus/client_golang/prometheus" "golang.org/x/xerrors" "gopkg.in/natefinch/lumberjack.v2" "cdr.dev/slog/v3" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/serpent" ) ``` 3 groups: standard library, 3rd partly libs, Coder libs. This PR makes the change across the codebase. The PR in the stack above modifies our formatting to maintain this state of affairs, and is a separate PR so it's possible to review that one in detail.
151 lines
4.6 KiB
Go
151 lines
4.6 KiB
Go
package oidctest_test
|
|
|
|
import (
|
|
"context"
|
|
"crypto"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/coreos/go-oidc/v3/oidc"
|
|
"github.com/golang-jwt/jwt/v4"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/oauth2"
|
|
"golang.org/x/xerrors"
|
|
|
|
"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.
|
|
// It is done all in memory with no actual network requests.
|
|
// nolint:bodyclose
|
|
func TestFakeIDPBasicFlow(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
fake := oidctest.NewFakeIDP(t,
|
|
oidctest.WithLogging(t, nil),
|
|
)
|
|
|
|
cfg := fake.OIDCConfig(t, nil)
|
|
cli := fake.HTTPClient(nil)
|
|
ctx := oidc.ClientContext(context.Background(), cli)
|
|
|
|
const expectedState = "random-state"
|
|
var token *oauth2.Token
|
|
// This is the Coder callback using an actual network request.
|
|
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, jwt.MapClaims{})
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
// Test the user info
|
|
_, err := cfg.Provider.UserInfo(ctx, oauth2.StaticTokenSource(token))
|
|
require.NoError(t, err)
|
|
|
|
// Now test it can refresh
|
|
refreshed, err := cfg.TokenSource(ctx, &oauth2.Token{
|
|
AccessToken: token.AccessToken,
|
|
RefreshToken: token.RefreshToken,
|
|
Expiry: time.Now().Add(time.Minute * -1),
|
|
}).Token()
|
|
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)
|
|
}
|