1
Fork 0

Attempt all keys when validating stateless tokens.

This commit is contained in:
Juliusz Chroboczek 2024-05-09 18:40:14 +02:00
parent f3ceb05033
commit 3a6d924374
3 changed files with 19 additions and 17 deletions

View File

@ -19,6 +19,8 @@ Galene 0.9 (unreleased)
* Added a new command "/stopshare".
* Added a new permission "message" and new commands "shutup" and "unshutup".
* Fixed a bug that could allow an ordinary user to clear the chat.
* Changed stateless token validation to attempt all keys rather than
just the first matching one.
14 April 2024: Galene 0.8.2

7
README
View File

@ -309,21 +309,18 @@ specify either an authorisation server or an authorisation portal.
"kty": "oct",
"alg": "HS256",
"k": "MYz3IfCq4Yq-UmPdNqWEOdPl4C_m9imHHs9uveDUJGQ",
"kid": "20211030"
}, {
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"x": "dElK9qBNyCpRXdvJsn4GdjrFzScSzpkz_I0JhKbYC88",
"y": "pBhVb37haKvwEoleoW3qxnT4y5bK35_RTP7_RmFKR6Q",
"kid": "20211101"
}]
"authServer": "https://auth.example.org",
}
The `kid` field serves to distinguish among multiple keys, and must match
the value provided by the authorisation server. If the server doesn't
provide a `kid`, the first key with a matching `alg` field will be used.
If multiple keys are provided, then they will all be tried in turn (the
kid field, if provided, is ignored).
If an authorisation server is specified, then the default client, after it
prompts for a password, will request a token from the authorisation server

View File

@ -28,7 +28,7 @@ func parseBase64(k string, d map[string]interface{}) ([]byte, error) {
return vv, nil
}
func ParseKey(key map[string]interface{}) (interface{}, error) {
func ParseKey(key map[string]any) (any, error) {
kty, ok := key["kty"].(string)
if !ok {
return nil, errors.New("kty not found")
@ -96,17 +96,16 @@ func ParseKey(key map[string]interface{}) (interface{}, error) {
}
}
func getKey(header map[string]interface{}, keys []map[string]interface{}) (interface{}, error) {
alg, _ := header["alg"].(string)
kid, _ := header["kid"].(string)
for _, k := range keys {
kid2, _ := k["kid"].(string)
alg2, _ := k["alg"].(string)
if (kid == "" || kid == kid2) && alg == alg2 {
return ParseKey(k)
func ParseKeys(keys []map[string]any) ([]jwt.VerificationKey, error) {
ks := make([]jwt.VerificationKey, len(keys))
for i, ky := range keys {
k, err := ParseKey(ky)
if err != nil {
return nil, err
}
ks[i] = k
}
return nil, errors.New("key not found")
return ks, nil
}
func toStringArray(a interface{}) ([]string, bool) {
@ -128,11 +127,15 @@ func toStringArray(a interface{}) ([]string, bool) {
// parseJWT tries to parse a string as a JWT.
// It returns (nil, nil) if the string does not look like a JWT.
func parseJWT(token string, keys []map[string]interface{}) (*JWT, error) {
func parseJWT(token string, keys []map[string]any) (*JWT, error) {
t, err := jwt.Parse(
token,
func(t *jwt.Token) (interface{}, error) {
return getKey(t.Header, keys)
ks, err := ParseKeys(keys)
if err != nil {
return nil, err
}
return jwt.VerificationKeySet{Keys: ks}, nil
},
jwt.WithExpirationRequired(),
jwt.WithIssuedAt(),