Skip to content

Commit 37a3c7f

Browse files
authored
Add support for encrypted request object (#42)
1 parent 87a59e7 commit 37a3c7f

File tree

8 files changed

+95
-56
lines changed

8 files changed

+95
-56
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ The available flags are:
6969
--auth-method string token endpoint authentication method
7070
--client-id string client identifier
7171
--client-secret string client secret
72+
--encrypted-request-object pass request parameters as encrypted jwt
7273
--encryption-key string path or url to encryption key in jwks format
7374
--grant-type string grant type
7475
-h, --help help for oauthc

cmd/log.go

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strconv"
1111
"strings"
1212

13+
"github.com/go-jose/go-jose/v3"
1314
"github.com/go-jose/go-jose/v3/jwt"
1415

1516
"github.com/cloudentity/oauth2c/internal/oauth2"
@@ -277,10 +278,11 @@ func LogJARM(request oauth2.Request) {
277278

278279
func LogRequestObject(r oauth2.Request) {
279280
var (
280-
request = r.URL.Query().Get("request")
281-
requestClaims map[string]interface{}
282-
token *jwt.JSONWebToken
283-
err error
281+
request = r.URL.Query().Get("request")
282+
requestClaims map[string]interface{}
283+
token *jwt.JSONWebToken
284+
encryptedToken *jose.JSONWebEncryption
285+
err error
284286
)
285287

286288
if request == "" {
@@ -292,10 +294,15 @@ func LogRequestObject(r oauth2.Request) {
292294
}
293295

294296
if request != "" {
295-
if token, requestClaims, err = oauth2.UnsafeParseJWT(request); err != nil {
297+
if token, requestClaims, err = oauth2.UnsafeParseJWT(r.RequestObject); err != nil {
296298
pterm.Error.Println(err)
297299
} else {
298-
pterm.DefaultBox.WithTitle("Request object").Printfln("request = JWT-%s(payload)", token.Headers[0].Algorithm)
300+
if encryptedToken, err = jose.ParseEncrypted(request); err == nil {
301+
pterm.DefaultBox.WithTitle("Request object").Printfln("request = JWE-%s(JWT-%s(payload))", encryptedToken.Header.Algorithm, token.Headers[0].Algorithm)
302+
} else {
303+
pterm.DefaultBox.WithTitle("Request object").Printfln("request = JWT-%s(payload)", token.Headers[0].Algorithm)
304+
}
305+
299306
pterm.Println()
300307
pterm.Println("Payload")
301308
LogJson(requestClaims)
@@ -348,6 +355,17 @@ func LogKey(name string, key interface{}) {
348355
pterm.Println(name)
349356

350357
switch key := key.(type) {
358+
case *rsa.PublicKey:
359+
p := bytes.Buffer{}
360+
361+
if err = pem.Encode(&p, &pem.Block{
362+
Type: "RSA PUBLIC KEY",
363+
Bytes: x509.MarshalPKCS1PublicKey(key),
364+
}); err != nil {
365+
pterm.Error.Println(err)
366+
}
367+
368+
pterm.FgGray.Printfln(p.String())
351369
case *rsa.PrivateKey:
352370
p := bytes.Buffer{}
353371

@@ -358,6 +376,23 @@ func LogKey(name string, key interface{}) {
358376
pterm.Error.Println(err)
359377
}
360378

379+
pterm.FgGray.Printfln(p.String())
380+
case *ecdsa.PublicKey:
381+
b, err := x509.MarshalPKIXPublicKey(key)
382+
383+
if err != nil {
384+
pterm.Error.Println(err)
385+
}
386+
387+
p := bytes.Buffer{}
388+
389+
if err = pem.Encode(&p, &pem.Block{
390+
Type: "EC PUBLIC KEY",
391+
Bytes: b,
392+
}); err != nil {
393+
pterm.Error.Println(err)
394+
}
395+
361396
pterm.FgGray.Printfln(p.String())
362397
case *ecdsa.PrivateKey:
363398
b, err := x509.MarshalECPrivateKey(key)

cmd/oauth2.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func NewOAuth2Cmd() (cmd *OAuth2Cmd) {
5858
cmd.PersistentFlags().BoolVar(&cconfig.PKCE, "pkce", false, "enable proof key for code exchange (PKCE)")
5959
cmd.PersistentFlags().BoolVar(&cconfig.PAR, "par", false, "enable pushed authorization requests (PAR)")
6060
cmd.PersistentFlags().BoolVar(&cconfig.RequestObject, "request-object", false, "pass request parameters as jwt")
61+
cmd.PersistentFlags().BoolVar(&cconfig.EncryptedRequestObject, "encrypted-request-object", false, "pass request parameters as encrypted jwt")
6162
cmd.PersistentFlags().StringVar(&cconfig.Assertion, "assertion", "", "claims for jwt bearer assertion")
6263
cmd.PersistentFlags().StringVar(&cconfig.SigningKey, "signing-key", "", "path or url to signing key in jwks format")
6364
cmd.PersistentFlags().StringVar(&cconfig.EncryptionKey, "encryption-key", "", "path or url to encryption key in jwks format")

internal/oauth2/jwe.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ import (
99

1010
type EncrypterProvider func() (jose.Encrypter, interface{}, error)
1111

12-
func JWEEncrypter(clientConfig ClientConfig, hc *http.Client) EncrypterProvider {
12+
func JWEEncrypter(keyPath string, hc *http.Client) EncrypterProvider {
1313
return func() (encrypter jose.Encrypter, _ interface{}, err error) {
1414
var key jose.JSONWebKey
1515

16-
if clientConfig.EncryptionKey == "" {
16+
if keyPath == "" {
1717
return nil, nil, errors.New("no encryption key path")
1818
}
1919

20-
if key, err = ReadKey(EncryptionKey, clientConfig.EncryptionKey, hc); err != nil {
21-
return nil, nil, errors.Wrapf(err, "failed to read encryption key from %s", clientConfig.EncryptionKey)
20+
if key, err = ReadKey(EncryptionKey, keyPath, hc); err != nil {
21+
return nil, nil, errors.Wrapf(err, "failed to read encryption key from %s", keyPath)
2222
}
2323

2424
if encrypter, err = jose.NewEncrypter(

internal/oauth2/jwt.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ func UnsafeParseJWT(token string) (*jwt.JSONWebToken, map[string]interface{}, er
3232

3333
type SignerProvider func() (jose.Signer, interface{}, error)
3434

35-
func JWKSigner(clientConfig ClientConfig, hc *http.Client) SignerProvider {
35+
func JWKSigner(keyPath string, hc *http.Client) SignerProvider {
3636
return func() (signer jose.Signer, _ interface{}, err error) {
3737
var key jose.JSONWebKey
3838

39-
if clientConfig.SigningKey == "" {
39+
if keyPath == "" {
4040
return nil, nil, errors.New("no signing key path")
4141
}
4242

43-
if key, err = ReadKey(SigningKey, clientConfig.SigningKey, hc); err != nil {
44-
return nil, nil, errors.Wrapf(err, "failed to read signing key from %s", clientConfig.SigningKey)
43+
if key, err = ReadKey(SigningKey, keyPath, hc); err != nil {
44+
return nil, nil, errors.Wrapf(err, "failed to read signing key from %s", keyPath)
4545
}
4646

4747
if signer, err = jose.NewSigner(jose.SigningKey{

internal/oauth2/jwt_test.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ func TestSignJWT(t *testing.T) {
2323
},
2424
)
2525

26-
jwt, _, err := SignJWT(claims, JWKSigner(ClientConfig{
27-
SigningKey: "../../data/key.json",
28-
}, http.DefaultClient))
26+
jwt, _, err := SignJWT(claims, JWKSigner("../../data/key.json", http.DefaultClient))
2927
require.NoError(t, err)
3028

3129
jws, err := jose.ParseSigned(jwt)

internal/oauth2/oauth2.go

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,31 +54,32 @@ const CodeVerifierLength = 43
5454
var CodeChallengeEncoder = base64.RawURLEncoding
5555

5656
type ClientConfig struct {
57-
IssuerURL string
58-
GrantType string
59-
ClientID string
60-
ClientSecret string
61-
Scopes []string
62-
AuthMethod string
63-
PKCE bool
64-
PAR bool
65-
RequestObject bool
66-
Insecure bool
67-
ResponseType []string
68-
ResponseMode string
69-
Username string
70-
Password string
71-
RefreshToken string
72-
Assertion string
73-
SigningKey string
74-
EncryptionKey string
75-
SubjectToken string
76-
SubjectTokenType string
77-
ActorToken string
78-
ActorTokenType string
79-
TLSCert string
80-
TLSKey string
81-
TLSRootCA string
57+
IssuerURL string
58+
GrantType string
59+
ClientID string
60+
ClientSecret string
61+
Scopes []string
62+
AuthMethod string
63+
PKCE bool
64+
PAR bool
65+
RequestObject bool
66+
EncryptedRequestObject bool
67+
Insecure bool
68+
ResponseType []string
69+
ResponseMode string
70+
Username string
71+
Password string
72+
RefreshToken string
73+
Assertion string
74+
SigningKey string
75+
EncryptionKey string
76+
SubjectToken string
77+
SubjectTokenType string
78+
ActorToken string
79+
ActorTokenType string
80+
TLSCert string
81+
TLSKey string
82+
TLSRootCA string
8283
}
8384

8485
func RequestAuthorization(addr string, cconfig ClientConfig, sconfig ServerConfig, hc *http.Client) (r Request, codeVerifier string, err error) {
@@ -358,7 +359,7 @@ func RequestToken(
358359

359360
if assertion, request.SigningKey, err = SignJWT(
360361
AssertionClaims(sconfig, cconfig),
361-
JWKSigner(cconfig, hc),
362+
JWKSigner(cconfig.SigningKey, hc),
362363
); err != nil {
363364
return request, response, err
364365
}

internal/oauth2/request.go

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type Request struct {
1919
Headers map[string][]string
2020
Form url.Values
2121
JARM map[string]interface{}
22+
RequestObject string
2223
SigningKey interface{}
2324
EncryptionKey interface{}
2425
Cert *x509.Certificate
@@ -64,33 +65,35 @@ func (r *Request) AuthorizeRequest(
6465
r.Form.Set("code_challenge_method", "S256")
6566
}
6667

67-
if cconfig.RequestObject {
68-
var request string
69-
68+
if cconfig.RequestObject || cconfig.EncryptedRequestObject {
7069
claims := RequestObjectClaims(r.Form, sconfig, cconfig)
7170

7271
if cconfig.SigningKey != "" {
73-
if request, r.SigningKey, err = SignJWT(claims, JWKSigner(cconfig, hc)); err != nil {
72+
if r.RequestObject, r.SigningKey, err = SignJWT(claims, JWKSigner(cconfig.SigningKey, hc)); err != nil {
7473
return "", err
7574
}
7675
} else {
77-
if request, r.SigningKey, err = PlaintextJWT(claims); err != nil {
78-
return "", err
79-
}
80-
}
81-
82-
if cconfig.EncryptionKey != "" {
83-
if request, r.EncryptionKey, err = EncryptJWT(request, JWEEncrypter(cconfig, hc)); err != nil {
76+
if r.RequestObject, r.SigningKey, err = PlaintextJWT(claims); err != nil {
8477
return "", err
8578
}
8679
}
8780

8881
r.Form = url.Values{
8982
"client_id": {cconfig.ClientID},
90-
"request": {request},
83+
"request": {r.RequestObject},
9184
"scope": {"openid"},
9285
}
9386

87+
if cconfig.EncryptedRequestObject {
88+
var encryptedRequestObject string
89+
90+
if encryptedRequestObject, r.EncryptionKey, err = EncryptJWT(r.RequestObject, JWEEncrypter(sconfig.JWKsURI, hc)); err != nil {
91+
return "", err
92+
}
93+
94+
r.Form.Set("request", encryptedRequestObject)
95+
}
96+
9497
if len(cconfig.Scopes) > 0 {
9598
r.Form.Set("scope", strings.Join(cconfig.Scopes, " "))
9699
}
@@ -129,7 +132,7 @@ func (r *Request) AuthenticateClient(
129132

130133
if clientAssertion, r.SigningKey, err = SignJWT(
131134
ClientAssertionClaims(sconfig, cconfig),
132-
JWKSigner(cconfig, hc),
135+
JWKSigner(cconfig.SigningKey, hc),
133136
); err != nil {
134137
return endpoint, err
135138
}

0 commit comments

Comments
 (0)