-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TT-12740 support for jwe tokens in OIDC (#420)
- Loading branch information
Showing
10 changed files
with
447 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package jwe | ||
|
||
import ( | ||
"crypto/rsa" | ||
"crypto/tls" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/go-jose/go-jose/v3" | ||
|
||
"github.com/markbates/goth/providers/openidConnect" | ||
) | ||
|
||
type Handler struct { | ||
Enabled bool `json:"Enabled"` | ||
PrivateKeyLocation string `json:"Private_key_location"` | ||
Key *tls.Certificate `json:"-"` | ||
} | ||
|
||
func (handler *Handler) Decrypt(token string) (string, error) { | ||
if !handler.Enabled { | ||
return token, nil | ||
} | ||
|
||
if handler.Key == nil { | ||
return "", errors.New("JWE Private Key not loaded") | ||
} | ||
|
||
privateKey := handler.Key.PrivateKey.(*rsa.PrivateKey) | ||
|
||
// Parse the serialized token | ||
jwe, err := jose.ParseEncrypted(token) | ||
if err != nil { | ||
return "", fmt.Errorf("error parsing JWE: %v", err) | ||
} | ||
|
||
// Decrypt the token | ||
decrypted, err := jwe.Decrypt(privateKey) | ||
if err != nil { | ||
return "", fmt.Errorf("error decrypting JWE: %v", err) | ||
} | ||
|
||
return string(decrypted), nil | ||
} | ||
|
||
func DecryptIDToken(jweHandler *Handler, JWTSession *openidConnect.Session) error { | ||
decryptedIDToken, err := jweHandler.Decrypt(JWTSession.IDToken) | ||
if err != nil { | ||
return err | ||
} | ||
JWTSession.IDToken = decryptedIDToken | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
package jwe | ||
|
||
import ( | ||
"crypto/rsa" | ||
"testing" | ||
|
||
"github.com/markbates/goth/providers/openidConnect" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
// Test case for Handler.Decrypt | ||
func TestHandler_Decrypt(t *testing.T) { | ||
// Generate a mock private key | ||
mockCert, err := GenerateMockPrivateKey() | ||
assert.NoError(t, err) | ||
|
||
// Create a valid JWE token for testing | ||
jweString, err := CreateJWE([]byte("test token"), mockCert.PrivateKey.(*rsa.PrivateKey).Public().(*rsa.PublicKey)) | ||
assert.NoError(t, err) | ||
|
||
tests := []struct { | ||
name string | ||
handler *Handler | ||
token string | ||
expected string | ||
expectError bool | ||
errorMessage string | ||
}{ | ||
{ | ||
name: "Disabled Handler", | ||
handler: &Handler{ | ||
Enabled: false, | ||
}, | ||
token: jweString, | ||
expected: jweString, | ||
expectError: false, | ||
}, | ||
{ | ||
name: "Key Not Loaded", | ||
handler: &Handler{ | ||
Enabled: true, | ||
Key: nil, | ||
}, | ||
token: jweString, | ||
expected: "", | ||
expectError: true, | ||
errorMessage: "JWE Private Key not loaded", | ||
}, | ||
{ | ||
name: "Successful Decryption", | ||
handler: &Handler{ | ||
Enabled: true, | ||
Key: mockCert, | ||
}, | ||
token: jweString, | ||
expected: "test token", | ||
expectError: false, | ||
}, | ||
{ | ||
name: "Invalid Token", | ||
handler: &Handler{ | ||
Enabled: true, | ||
Key: mockCert, | ||
}, | ||
token: "invalid-token", | ||
expected: "", | ||
expectError: true, | ||
errorMessage: "error parsing JWE", | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
decrypted, err := tt.handler.Decrypt(tt.token) | ||
|
||
if tt.expectError { | ||
assert.Error(t, err) | ||
assert.Contains(t, err.Error(), tt.errorMessage) | ||
} else { | ||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expected, decrypted) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestDecryptIDToken(t *testing.T) { | ||
mockCert, err := GenerateMockPrivateKey() | ||
assert.NoError(t, err) | ||
|
||
// Create a valid JWE token for testing | ||
jweString, err := CreateJWE([]byte("test token"), mockCert.PrivateKey.(*rsa.PrivateKey).Public().(*rsa.PublicKey)) | ||
assert.NoError(t, err) | ||
|
||
// Setup a valid JWE handler | ||
jweHandler := &Handler{ | ||
Enabled: true, | ||
Key: mockCert, | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
jwtSession *openidConnect.Session | ||
expectError bool | ||
expectedToken string | ||
jweHandler *Handler | ||
}{ | ||
{ | ||
name: "Successful Decryption", | ||
jwtSession: &openidConnect.Session{ | ||
IDToken: jweString, | ||
}, | ||
expectError: false, | ||
expectedToken: "test token", | ||
jweHandler: jweHandler, | ||
}, | ||
{ | ||
name: "Invalid Token", | ||
jwtSession: &openidConnect.Session{ | ||
IDToken: "invalid-token", | ||
}, | ||
expectError: true, | ||
jweHandler: jweHandler, | ||
}, | ||
{ | ||
name: "Failed Decryption due Key not loaded", | ||
jwtSession: &openidConnect.Session{ | ||
IDToken: jweString, | ||
}, | ||
expectError: true, | ||
expectedToken: "test token", | ||
jweHandler: &Handler{ | ||
Enabled: true, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
err := DecryptIDToken(tt.jweHandler, tt.jwtSession) | ||
|
||
if tt.expectError { | ||
assert.Error(t, err) | ||
} else { | ||
assert.NoError(t, err) | ||
assert.Equal(t, tt.expectedToken, tt.jwtSession.IDToken) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package jwe | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/tls" | ||
|
||
"github.com/go-jose/go-jose/v3" | ||
) | ||
|
||
func GenerateMockPrivateKey() (*tls.Certificate, error) { | ||
// Generate a new RSA private key for testing | ||
privKey, err := rsa.GenerateKey(rand.Reader, 2048) | ||
if err != nil { | ||
return nil, err | ||
} | ||
cert := &tls.Certificate{ | ||
PrivateKey: privKey, | ||
} | ||
return cert, nil | ||
} | ||
|
||
func CreateJWE(payload []byte, recipient *rsa.PublicKey) (string, error) { | ||
|
||
encrypter, err := jose.NewEncrypter( | ||
jose.A256GCM, | ||
jose.Recipient{ | ||
Algorithm: jose.RSA_OAEP_256, | ||
Key: recipient, | ||
}, | ||
(&jose.EncrypterOptions{}).WithType("JWT")) | ||
if err != nil { | ||
return "", err | ||
} | ||
jwe, err := encrypter.Encrypt(payload) | ||
if err != nil { | ||
return "", err | ||
} | ||
return jwe.CompactSerialize() | ||
} |
Oops, something went wrong.