Commit 3a6d9243 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Attempt all keys when validating stateless tokens.

parent f3ceb050
......@@ -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
......
......@@ -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
......
......@@ -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(),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment