Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for DeviceAuthURL and a Provider.Config() API #140

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions oidc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ func (c *Config) Validate() error {
return fmt.Errorf("%s: missing TokenURL: %w", op, ErrInvalidParameter)
case c.ProviderConfig.UserInfoURL == "":
return fmt.Errorf("%s: missing UserInfoURL: %w", op, ErrInvalidParameter)
// DeviceAuthURL is optional
}
}
return nil
Expand Down Expand Up @@ -400,6 +401,7 @@ type ProviderConfig struct {

// TokenURL is the provider's OAuth2.0 token endpoint.
TokenURL string

// UserInfoURL is the provider's OpenID UserInfo endpoint.
//
// See: https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
Expand All @@ -408,6 +410,9 @@ type ProviderConfig struct {
// JWKSURL is the provider's OpenID JWKS endpoint (where it publishes the
// pub keys.
JWKSURL string

// DeviceAuthURL is the provider's optional device authorization endpoint.
DeviceAuthURL string
}

// WithProviderConfig provides an optional ProviderConfig which supports
Expand Down
18 changes: 10 additions & 8 deletions oidc/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ func TestNewConfig(t *testing.T) {
WithProviderCA(testCaPem),
WithNow(testNow),
WithProviderConfig(&ProviderConfig{
AuthURL: "https://auth-endpoint",
JWKSURL: "https://jwks-endpoint",
TokenURL: "https://token-endpoint",
UserInfoURL: "https://userinfo-endpoint",
AuthURL: "https://auth-endpoint",
JWKSURL: "https://jwks-endpoint",
TokenURL: "https://token-endpoint",
UserInfoURL: "https://userinfo-endpoint",
DeviceAuthURL: "https://deviceauth-endpoint",
}),
},
},
Expand All @@ -99,10 +100,11 @@ func TestNewConfig(t *testing.T) {
"http://redirect_url_three",
},
ProviderConfig: &ProviderConfig{
AuthURL: "https://auth-endpoint",
JWKSURL: "https://jwks-endpoint",
TokenURL: "https://token-endpoint",
UserInfoURL: "https://userinfo-endpoint",
AuthURL: "https://auth-endpoint",
JWKSURL: "https://jwks-endpoint",
TokenURL: "https://token-endpoint",
UserInfoURL: "https://userinfo-endpoint",
DeviceAuthURL: "https://deviceauth-endpoint",
},
},
},
Expand Down
11 changes: 6 additions & 5 deletions oidc/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,11 @@ func ExampleWithProviderConfig() {
[]oidc.Alg{oidc.RS256},
[]string{"https://your_redirect_url/callback"},
oidc.WithProviderConfig(&oidc.ProviderConfig{
AuthURL: "https://your_issuer/authorize",
TokenURL: "https://your_issuer/token",
JWKSURL: "https://your_issuer/.well-known/jwks.json",
UserInfoURL: "https://your_issuer/userinfo",
AuthURL: "https://your_issuer/authorize",
TokenURL: "https://your_issuer/token",
DeviceAuthURL: "https://your_issuer/authorize",
JWKSURL: "https://your_issuer/.well-known/jwks.json",
UserInfoURL: "https://your_issuer/userinfo",
}),
)
if err != nil {
Expand All @@ -120,7 +121,7 @@ func ExampleWithProviderConfig() {
fmt.Println(string(val))

// Output:
// {"ClientID":"your_client_id","ClientSecret":"[REDACTED: client secret]","Scopes":["openid"],"Issuer":"https://your_issuer/","SupportedSigningAlgs":["RS256"],"AllowedRedirectURLs":["https://your_redirect_url/callback"],"Audiences":null,"ProviderCA":"","RoundTripper":null,"ProviderConfig":{"AuthURL":"https://your_issuer/authorize","TokenURL":"https://your_issuer/token","UserInfoURL":"https://your_issuer/userinfo","JWKSURL":"https://your_issuer/.well-known/jwks.json"}}
// {"ClientID":"your_client_id","ClientSecret":"[REDACTED: client secret]","Scopes":["openid"],"Issuer":"https://your_issuer/","SupportedSigningAlgs":["RS256"],"AllowedRedirectURLs":["https://your_redirect_url/callback"],"Audiences":null,"ProviderCA":"","RoundTripper":null,"ProviderConfig":{"AuthURL":"https://your_issuer/authorize","TokenURL":"https://your_issuer/token","UserInfoURL":"https://your_issuer/userinfo","JWKSURL":"https://your_issuer/.well-known/jwks.json","DeviceAuthURL":"https://your_issuer/authorize"}}
}

func ExampleNewProvider() {
Expand Down
34 changes: 28 additions & 6 deletions oidc/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,13 @@ func NewProvider(c *Config) (*Provider, error) {
convertedAlgs = append(convertedAlgs, string(alg))
}
cfg := oidc.ProviderConfig{
IssuerURL: c.Issuer,
AuthURL: c.ProviderConfig.AuthURL,
JWKSURL: c.ProviderConfig.JWKSURL,
TokenURL: c.ProviderConfig.TokenURL,
UserInfoURL: c.ProviderConfig.UserInfoURL,
Algorithms: convertedAlgs,
IssuerURL: c.Issuer,
AuthURL: c.ProviderConfig.AuthURL,
JWKSURL: c.ProviderConfig.JWKSURL,
TokenURL: c.ProviderConfig.TokenURL,
DeviceAuthURL: c.ProviderConfig.DeviceAuthURL,
UserInfoURL: c.ProviderConfig.UserInfoURL,
Algorithms: convertedAlgs,
}
p.provider = cfg.NewProvider(oidcCtx)

Expand Down Expand Up @@ -725,6 +726,9 @@ type DiscoveryInfo struct {
// TokenURL (REQUIRED): URL of the OP's OAuth 2.0 Token Endpoint
TokenURL string `json:"token_endpoint"`

// DeviceAuthURL (OPTIONAL, omitempty): URL of the OP's Device Authorization Endpoint
DeviceAuthURL string `json:"device_authorization_endpoint,omitempty"`

// UserInfoURL (OPTIONAL, omitempty): URL of the OP's UserInfo Endpoint
UserInfoURL string `json:"userinfo_endpoint,omitempty"`

Expand Down Expand Up @@ -826,3 +830,21 @@ func unmarshalRespJSON(r *http.Response, body []byte, v interface{}) error {
}
return fmt.Errorf("%s: expected Content-Type = application/json, got %q and could not unmarshal it as JSON: %w", op, ct, err)
}

// Config returns the ProviderConfig for this Provider.
// If it was supplied in the first place, just return it, but otherwise
// construct it from what was discovered by and available from go-oidc.
func (p *Provider) Config() ProviderConfig {
providerConfig := p.config.ProviderConfig
if providerConfig == nil {
// Note that JWKSURL is not available through the go-oidc api
endpoint := p.provider.Endpoint()
providerConfig = &ProviderConfig{
AuthURL: endpoint.AuthURL,
TokenURL: endpoint.TokenURL,
DeviceAuthURL: endpoint.DeviceAuthURL,
UserInfoURL: p.provider.UserInfoEndpoint(),
}
}
return *providerConfig
}
69 changes: 46 additions & 23 deletions oidc/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1585,64 +1585,71 @@ func TestProvider_DiscoveryInfo(t *testing.T) {
ctx := context.Background()

tests := []struct {
name string
data string
trailingSlashIss bool
overrideHeader string
overrideData string
overrideIssuer string
wantAuthURL string
wantTokenURL string
wantUserInfoURL string
wantAlgorithms []string
wantScopes []string
wantErr bool
wantErrContains string
name string
data string
trailingSlashIss bool
overrideHeader string
overrideData string
overrideIssuer string
wantAuthURL string
wantTokenURL string
wantDeviceAuthURL string
wantUserInfoURL string
wantAlgorithms []string
wantScopes []string
wantErr bool
wantErrContains string
}{
{
name: "basic_case",
data: `{
"issuer": "ISSUER",
"authorization_endpoint": "https://example.com/auth",
"token_endpoint": "https://example.com/token",
"device_authorization_endpoint": "https://example.com/deviceauth",
"jwks_uri": "https://example.com/keys",
"id_token_signing_alg_values_supported": ["RS256", "RS384"],
"scopes_supported": ["openid", "profile"]
}`,
wantScopes: []string{"openid", "profile"},
wantAuthURL: "https://example.com/auth",
wantTokenURL: "https://example.com/token",
wantAlgorithms: []string{"RS256", "RS384"},
wantScopes: []string{"openid", "profile"},
wantAuthURL: "https://example.com/auth",
wantTokenURL: "https://example.com/token",
wantDeviceAuthURL: "https://example.com/deviceauth",
wantAlgorithms: []string{"RS256", "RS384"},
},
{
name: "basic_case",
name: "basic_case2",
data: `{
"issuer": "ISSUER",
"authorization_endpoint": "https://example.com/auth",
"token_endpoint": "https://example.com/token",
"device_authorization_endpoint": "https://example.com/deviceauth",
"jwks_uri": "https://example.com/keys",
"id_token_signing_alg_values_supported": ["RS256", "RS384"],
"scopes_supported": ["openid", "profile"]
}`,
trailingSlashIss: true,
wantScopes: []string{"openid", "profile"},
wantAuthURL: "https://example.com/auth",
wantTokenURL: "https://example.com/token",
wantAlgorithms: []string{"RS256", "RS384"},
trailingSlashIss: true,
wantScopes: []string{"openid", "profile"},
wantAuthURL: "https://example.com/auth",
wantTokenURL: "https://example.com/token",
wantDeviceAuthURL: "https://example.com/deviceauth",
wantAlgorithms: []string{"RS256", "RS384"},
},
{
name: "mismatched_issuer",
data: `{
"issuer": "ISSUER",
"authorization_endpoint": "https://example.com/auth",
"token_endpoint": "https://example.com/token",
"device_authorization_endpoint": "https://example.com/deviceauth",
"jwks_uri": "https://example.com/keys",
"id_token_signing_alg_values_supported": ["RS256"]
}`,
overrideData: `{
"issuer": "https://example.com",
"authorization_endpoint": "https://example.com/auth",
"token_endpoint": "https://example.com/token",
"device_authorization_endpoint": "https://example.com/deviceauth",
"jwks_uri": "https://example.com/keys",
"id_token_signing_alg_values_supported": ["RS256"]
}`,
Expand All @@ -1655,6 +1662,7 @@ func TestProvider_DiscoveryInfo(t *testing.T) {
"issuer": "ISSUER",
"authorization_endpoint": "https://example.com/auth",
"token_endpoint": "https://example.com/token",
"device_authorization_endpoint": "https://example.com/deviceauth",
"jwks_uri": "https://example.com/keys",
"id_token_signing_alg_values_supported": ["RS256"]
}`,
Expand All @@ -1668,6 +1676,7 @@ func TestProvider_DiscoveryInfo(t *testing.T) {
"issuer": "ISSUER",
"authorization_endpoint": "https://example.com/auth",
"token_endpoint": "https://example.com/token",
"device_authorization_endpoint": "https://example.com/deviceauth",
"jwks_uri": "https://example.com/keys",
"id_token_signing_alg_values_supported": ["RS256"]
}`,
Expand Down Expand Up @@ -1725,9 +1734,23 @@ func TestProvider_DiscoveryInfo(t *testing.T) {
require.NoError(err)
assert.Equal(info.AuthURL, tt.wantAuthURL)
assert.Equal(info.TokenURL, tt.wantTokenURL)
assert.Equal(info.DeviceAuthURL, tt.wantDeviceAuthURL)
assert.Equal(info.UserInfoURL, tt.wantUserInfoURL)
assert.Equal(info.IdTokenSigningAlgsSupported, tt.wantAlgorithms)
assert.Equal(info.ScopesSupported, tt.wantScopes)
})
}
}

func TestProvider_Config(t *testing.T) {
t.Parallel()
tp := StartTestProvider(t)
p := testNewProvider(t, "client-id", "client-secret", "redirect", tp)

c := p.Config()
assert := assert.New(t)
assert.NotEqual(c.AuthURL, "")
assert.NotEqual(c.DeviceAuthURL, "")
assert.NotEqual(c.TokenURL, "")
assert.NotEqual(c.UserInfoURL, "")
}
3 changes: 3 additions & 0 deletions oidc/testing_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,7 @@ func (p *TestProvider) ServeHTTP(w http.ResponseWriter, req *http.Request) {
const (
openidConfiguration = "/.well-known/openid-configuration"
authorize = "/authorize"
deviceAuthorize = "/deviceauthorize"
token = "/token"
userInfo = "/userinfo"
wellKnownJwks = "/.well-known/jwks.json"
Expand Down Expand Up @@ -1160,6 +1161,7 @@ func (p *TestProvider) ServeHTTP(w http.ResponseWriter, req *http.Request) {
reply := struct {
Issuer string `json:"issuer"`
AuthEndpoint string `json:"authorization_endpoint"`
DeviceAuthEndpoint string `json:"device_authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
JWKSURI string `json:"jwks_uri"`
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
Expand All @@ -1170,6 +1172,7 @@ func (p *TestProvider) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}{
Issuer: p.Addr(),
AuthEndpoint: p.Addr() + authorize,
DeviceAuthEndpoint: p.Addr() + deviceAuthorize,
TokenEndpoint: p.Addr() + token,
JWKSURI: p.Addr() + wellKnownJwks,
UserinfoEndpoint: p.Addr() + userInfo,
Expand Down