Skip to content

Commit bbe0b8a

Browse files
authored
Make it possible to configure server endpoints manually (#110)
1 parent f6c72a2 commit bbe0b8a

File tree

7 files changed

+137
-65
lines changed

7 files changed

+137
-65
lines changed

README.md

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -83,45 +83,54 @@ oauth2c [issuer url] [flags]
8383
The available flags are:
8484

8585
```sh
86-
--acr-values strings ACR values
87-
--actor-token string acting party token
88-
--actor-token-type string acting party token type
89-
--assertion string claims for jwt bearer assertion
90-
--audience strings requested audience
91-
--auth-method string token endpoint authentication method
92-
--browser-timeout duration browser timeout (default 10m0s)
93-
--claims string use claims
94-
--client-id string client identifier
95-
--client-secret string client secret
96-
--dpop use DPoP
97-
--encrypted-request-object pass request parameters as encrypted jwt
98-
--encryption-key string path or url to encryption key in jwks format
99-
--grant-type string grant type
100-
-h, --help help for oauthc
101-
--http-timeout duration http client timeout (default 1m0s)
102-
--id-token-hint string id token hint
103-
--idp-hint string identity provider hint
104-
--insecure allow insecure connections
105-
--login-hint string user identifier hint
106-
--no-prompt disable prompt
107-
--par enable pushed authorization requests (PAR)
108-
--password string resource owner password credentials grant flow password
109-
--pkce enable proof key for code exchange (PKCE)
110-
--rar string use rich authorization request (RAR)
111-
--redirect-url string client redirect url (default "http://localhost:9876/callback")
112-
--refresh-token string refresh token
113-
--request-object pass request parameters as jwt
114-
--response-mode string response mode
115-
--response-types strings response type
116-
--scopes strings requested scopes
117-
--signing-key string path or url to signing key in jwks format
118-
-s, --silent silent mode
119-
--subject-token string third party token
120-
--subject-token-type string third party token type
121-
--tls-cert string path to tls cert pem file
122-
--tls-key string path to tls key pem file
123-
--tls-root-ca string path to tls root ca pem file
124-
--username string resource owner password credentials grant flow username
86+
--acr-values strings ACR values
87+
--actor-token string acting party token
88+
--actor-token-type string acting party token type
89+
--assertion string claims for jwt bearer assertion
90+
--audience strings requested audience
91+
--auth-method string token endpoint authentication method
92+
--authorization-endpoint string server's authorization endpoint
93+
--browser-timeout duration browser timeout (default 10m0s)
94+
--callback-tls-cert string path to callback tls cert pem file
95+
--callback-tls-key string path to callback tls key pem file
96+
--claims string use claims
97+
--client-id string client identifier
98+
--client-secret string client secret
99+
--device-authorization-endpoint string server's device authorization endpoint
100+
--dpop use DPoP
101+
--encrypted-request-object pass request parameters as encrypted jwt
102+
--encryption-key string path or url to encryption key in jwks format
103+
--grant-type string grant type
104+
-h, --help help for oauth2c
105+
--http-timeout duration http client timeout (default 1m0s)
106+
--id-token-hint string id token hint
107+
--idp-hint string identity provider hint
108+
--insecure allow insecure connections
109+
--login-hint string user identifier hint
110+
--mtls-pushed-authorization-request-endpoint string server's mtls pushed authorization request endpoint
111+
--mtls-token-endpoint string server's mtls token endpoint
112+
--no-prompt disable prompt
113+
--par enable pushed authorization requests (PAR)
114+
--password string resource owner password credentials grant flow password
115+
--pkce enable proof key for code exchange (PKCE)
116+
--purpose string string describing the purpose for obtaining End-User authorization
117+
--pushed-authorization-request-endpoint string server's pushed authorization request endpoint
118+
--rar string use rich authorization request (RAR)
119+
--redirect-url string client redirect url (default "http://localhost:9876/callback")
120+
--refresh-token string refresh token
121+
--request-object pass request parameters as jwt
122+
--response-mode string response mode
123+
--response-types strings response type
124+
--scopes strings requested scopes
125+
--signing-key string path or url to signing key in jwks format
126+
-s, --silent silent mode
127+
--subject-token string third party token
128+
--subject-token-type string third party token type
129+
--tls-cert string path to tls cert pem file
130+
--tls-key string path to tls key pem file
131+
--tls-root-ca string path to tls root ca pem file
132+
--token-endpoint string server's token endpoint
133+
--username string resource owner password credentials grant flow username
125134
```
126135

127136
`oauth2c` opens a browser for flows such as authorization code and starts an
@@ -710,6 +719,22 @@ oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
710719
--callback-tls-key https://raw.githubusercontent.com/cloudentity/oauth2c/master/data/key.pem
711720
```
712721

722+
#### Specifying Authorization Server's Endpoint Manually
723+
724+
If your authorization server does not support OIDC, you can specify the endpoint manually using flags.
725+
726+
```sh
727+
oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
728+
--client-id cauktionbud6q8ftlqq0 \
729+
--client-secret HCwQ5uuUWBRHd04ivjX5Kl0Rz8zxMOekeLtqzki0GPc \
730+
--response-types code \
731+
--response-mode query \
732+
--grant-type authorization_code \
733+
--auth-method client_secret_basic \
734+
--token-endpoint https://oauth2c.us.authz.cloudentity.io/oauth2c/demo/oauth2/token \
735+
--authorization-endpoint https://oauth2c.us.authz.cloudentity.io/oauth2c/demo/oauth2/authorize
736+
```
737+
713738
## License
714739

715740
`oauth2c` is released under the

cmd/oauth2.go

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ type OAuth2Cmd struct {
2828
}
2929

3030
func NewOAuth2Cmd(version, commit, date string) (cmd *OAuth2Cmd) {
31-
var cconfig oauth2.ClientConfig
31+
var (
32+
cconfig oauth2.ClientConfig
33+
sconfig oauth2.ServerConfig
34+
)
3235

3336
cmd = &OAuth2Cmd{
3437
Command: &cobra.Command{
@@ -38,7 +41,7 @@ func NewOAuth2Cmd(version, commit, date string) (cmd *OAuth2Cmd) {
3841
},
3942
}
4043

41-
cmd.Command.Run = cmd.Run(&cconfig)
44+
cmd.Command.Run = cmd.Run(&cconfig, &sconfig)
4245

4346
cmd.AddCommand(NewVersionCmd(version, commit, date))
4447
cmd.AddCommand(docsCmd)
@@ -85,10 +88,17 @@ func NewOAuth2Cmd(version, commit, date string) (cmd *OAuth2Cmd) {
8588
cmd.PersistentFlags().StringSliceVar(&cconfig.ACRValues, "acr-values", []string{}, "ACR values")
8689
cmd.PersistentFlags().StringVar(&cconfig.Purpose, "purpose", "", "string describing the purpose for obtaining End-User authorization")
8790

91+
cmd.PersistentFlags().StringVar(&sconfig.TokenEndpoint, "token-endpoint", "", "server's token endpoint")
92+
cmd.PersistentFlags().StringVar(&sconfig.AuthorizationEndpoint, "authorization-endpoint", "", "server's authorization endpoint")
93+
cmd.PersistentFlags().StringVar(&sconfig.DeviceAuthorizationEndpoint, "device-authorization-endpoint", "", "server's device authorization endpoint")
94+
cmd.PersistentFlags().StringVar(&sconfig.PushedAuthorizationRequestEndpoint, "pushed-authorization-request-endpoint", "", "server's pushed authorization request endpoint")
95+
cmd.PersistentFlags().StringVar(&sconfig.MTLsEndpointAliases.TokenEndpoint, "mtls-token-endpoint", "", "server's mtls token endpoint")
96+
cmd.PersistentFlags().StringVar(&sconfig.MTLsEndpointAliases.PushedAuthorizationRequestEndpoint, "mtls-pushed-authorization-request-endpoint", "", "server's mtls pushed authorization request endpoint")
97+
8898
return cmd
8999
}
90100

91-
func (c *OAuth2Cmd) Run(cconfig *oauth2.ClientConfig) func(cmd *cobra.Command, args []string) {
101+
func (c *OAuth2Cmd) Run(cconfig *oauth2.ClientConfig, sconfig *oauth2.ServerConfig) func(cmd *cobra.Command, args []string) {
92102
return func(cmd *cobra.Command, args []string) {
93103
var (
94104
config Config
@@ -148,7 +158,7 @@ func (c *OAuth2Cmd) Run(cconfig *oauth2.ClientConfig) func(cmd *cobra.Command, a
148158
}
149159
}
150160

151-
if err := c.Authorize(*cconfig, hc); err != nil {
161+
if err := c.Authorize(*cconfig, *sconfig, hc); err != nil {
152162
var oauth2Error *oauth2.Error
153163

154164
if errors.As(err, &oauth2Error) {
@@ -164,21 +174,26 @@ func (c *OAuth2Cmd) Run(cconfig *oauth2.ClientConfig) func(cmd *cobra.Command, a
164174
}
165175
}
166176

167-
func (c *OAuth2Cmd) Authorize(clientConfig oauth2.ClientConfig, hc *http.Client) error {
177+
func (c *OAuth2Cmd) Authorize(
178+
clientConfig oauth2.ClientConfig,
179+
serverConfig oauth2.ServerConfig,
180+
hc *http.Client,
181+
) error {
168182
var (
169183
serverRequest oauth2.Request
170-
serverConfig oauth2.ServerConfig
171184
err error
172185
)
173186

174187
// openid configuration
175-
if serverRequest, serverConfig, err = oauth2.FetchOpenIDConfiguration(
176-
context.Background(),
177-
clientConfig.IssuerURL,
178-
hc,
179-
); err != nil {
180-
LogRequestln(serverRequest)
181-
return err
188+
if !serverConfig.IsConfigured() {
189+
if serverRequest, serverConfig, err = oauth2.FetchOpenIDConfiguration(
190+
context.Background(),
191+
clientConfig.IssuerURL,
192+
hc,
193+
); err != nil {
194+
LogRequestln(serverRequest)
195+
return err
196+
}
182197
}
183198

184199
if !silent && !noPrompt {

internal/oauth2/jwt.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func RequestObjectClaims(params url.Values, serverConfig ServerConfig, clientCon
102102
return func() (map[string]interface{}, error) {
103103
claims := map[string]interface{}{
104104
"iss": clientConfig.ClientID,
105-
"aud": serverConfig.Issuer,
105+
"aud": clientConfig.IssuerURL,
106106
"exp": time.Now().Add(time.Minute * 10).Unix(),
107107
"nbf": time.Now().Unix(),
108108
}

internal/oauth2/jwt_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ func TestSignJWT(t *testing.T) {
1515

1616
claims := AssertionClaims(
1717
ServerConfig{
18-
Issuer: "https://example.com/tid/aid",
1918
TokenEndpoint: "https://example.com/tid/aid/oauth2/token",
2019
},
2120
ClientConfig{
21+
IssuerURL: "https://example.com/tid/aid",
2222
Assertion: `{"sub": "[email protected]"}`,
2323
},
2424
)

internal/oauth2/oauth2.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ type ClientConfig struct {
9595
}
9696

9797
func RequestAuthorization(cconfig ClientConfig, sconfig ServerConfig, hc *http.Client) (r Request, codeVerifier string, err error) {
98+
if sconfig.AuthorizationEndpoint == "" {
99+
return r, "", errors.New("the server's authorization endpoint is not configured")
100+
}
101+
98102
if r.URL, err = url.Parse(sconfig.AuthorizationEndpoint); err != nil {
99103
return r, "", errors.Wrapf(err, "failed to parse authorization endpoint")
100104
}
@@ -127,6 +131,14 @@ func RequestPAR(
127131
endpoint string
128132
)
129133

134+
if sconfig.AuthorizationEndpoint == "" {
135+
return parRequest, parResponse, authorizeRequest, "", errors.New("the server's authorization endpoint is not configured")
136+
}
137+
138+
if sconfig.PushedAuthorizationRequestEndpoint == "" && sconfig.MTLsEndpointAliases.PushedAuthorizationRequestEndpoint == "" {
139+
return parRequest, parResponse, authorizeRequest, "", errors.New("the server's pushed authorization request endpoint is not configured")
140+
}
141+
130142
// push authorization request to /par
131143
if codeVerifier, err = parRequest.AuthorizeRequest(cconfig, sconfig, hc); err != nil {
132144
return parRequest, parResponse, authorizeRequest, "", errors.Wrapf(err, "failed to create authorization request")
@@ -382,6 +394,10 @@ func RequestToken(
382394
body []byte
383395
)
384396

397+
if sconfig.TokenEndpoint == "" && sconfig.MTLsEndpointAliases.TokenEndpoint == "" {
398+
return request, response, errors.New("the server's token endpoint is not configured")
399+
}
400+
385401
for _, opt := range opts {
386402
opt(&params)
387403
}

internal/oauth2/oauth2_device.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"net/http"
88
"net/url"
99
"strings"
10+
11+
"github.com/pkg/errors"
1012
)
1113

1214
type DeviceAuthorizationResponse struct {
@@ -24,6 +26,10 @@ func RequestDeviceAuthorization(ctx context.Context, cconfig ClientConfig, sconf
2426
resp *http.Response
2527
)
2628

29+
if sconfig.DeviceAuthorizationEndpoint == "" {
30+
return request, response, errors.New("the server's device authorization endpoint is not configured")
31+
}
32+
2733
request.Form = url.Values{
2834
"client_id": {cconfig.ClientID},
2935
}

internal/oauth2/well_known.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,31 @@ import (
99
const OpenIDConfigurationPath = "/.well-known/openid-configuration"
1010

1111
type ServerConfig struct {
12-
Issuer string `json:"issuer"`
13-
JWKsURI string `json:"jwks_uri"`
14-
SupportedGrantTypes []string `json:"grant_types_supported"`
15-
SupportedResponseTypes []string `json:"response_types_supported"`
16-
SupportedTokenEndpointAuthMethods []string `json:"token_endpoint_auth_methods_supported"`
17-
SupportedScopes []string `json:"scopes_supported"`
18-
SupportedResponseModes []string `json:"response_modes_supported"`
19-
AuthorizationEndpoint string `json:"authorization_endpoint"`
20-
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint"`
21-
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint"`
22-
TokenEndpoint string `json:"token_endpoint"`
12+
SupportedGrantTypes []string `json:"grant_types_supported"`
13+
SupportedResponseTypes []string `json:"response_types_supported"`
14+
SupportedTokenEndpointAuthMethods []string `json:"token_endpoint_auth_methods_supported"`
15+
SupportedScopes []string `json:"scopes_supported"`
16+
SupportedResponseModes []string `json:"response_modes_supported"`
17+
18+
AuthorizationEndpoint string `json:"authorization_endpoint"`
19+
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint"`
20+
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint"`
21+
TokenEndpoint string `json:"token_endpoint"`
2322
MTLsEndpointAliases struct {
2423
TokenEndpoint string `json:"token_endpoint"`
2524
PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint"`
2625
} `json:"mtls_endpoint_aliases"`
26+
27+
JWKsURI string `json:"jwks_uri"`
28+
}
29+
30+
func (c ServerConfig) IsConfigured() bool {
31+
return c.AuthorizationEndpoint != "" ||
32+
c.DeviceAuthorizationEndpoint != "" ||
33+
c.PushedAuthorizationRequestEndpoint != "" ||
34+
c.TokenEndpoint != "" ||
35+
c.MTLsEndpointAliases.TokenEndpoint != "" ||
36+
c.MTLsEndpointAliases.PushedAuthorizationRequestEndpoint != ""
2737
}
2838

2939
func FetchOpenIDConfiguration(ctx context.Context, issuerURL string, hc *http.Client) (request Request, c ServerConfig, err error) {

0 commit comments

Comments
 (0)