mirror of
https://github.com/jech/galene.git
synced 2024-11-22 08:35:57 +01:00
Attempt all keys when validating stateless tokens.
This commit is contained in:
parent
f3ceb05033
commit
3a6d924374
3 changed files with 19 additions and 17 deletions
2
CHANGES
2
CHANGES
|
@ -19,6 +19,8 @@ Galene 0.9 (unreleased)
|
||||||
* Added a new command "/stopshare".
|
* Added a new command "/stopshare".
|
||||||
* Added a new permission "message" and new commands "shutup" and "unshutup".
|
* Added a new permission "message" and new commands "shutup" and "unshutup".
|
||||||
* Fixed a bug that could allow an ordinary user to clear the chat.
|
* 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
|
14 April 2024: Galene 0.8.2
|
||||||
|
|
||||||
|
|
7
README
7
README
|
@ -309,21 +309,18 @@ specify either an authorisation server or an authorisation portal.
|
||||||
"kty": "oct",
|
"kty": "oct",
|
||||||
"alg": "HS256",
|
"alg": "HS256",
|
||||||
"k": "MYz3IfCq4Yq-UmPdNqWEOdPl4C_m9imHHs9uveDUJGQ",
|
"k": "MYz3IfCq4Yq-UmPdNqWEOdPl4C_m9imHHs9uveDUJGQ",
|
||||||
"kid": "20211030"
|
|
||||||
}, {
|
}, {
|
||||||
"kty": "EC",
|
"kty": "EC",
|
||||||
"alg": "ES256",
|
"alg": "ES256",
|
||||||
"crv": "P-256",
|
"crv": "P-256",
|
||||||
"x": "dElK9qBNyCpRXdvJsn4GdjrFzScSzpkz_I0JhKbYC88",
|
"x": "dElK9qBNyCpRXdvJsn4GdjrFzScSzpkz_I0JhKbYC88",
|
||||||
"y": "pBhVb37haKvwEoleoW3qxnT4y5bK35_RTP7_RmFKR6Q",
|
"y": "pBhVb37haKvwEoleoW3qxnT4y5bK35_RTP7_RmFKR6Q",
|
||||||
"kid": "20211101"
|
|
||||||
}]
|
}]
|
||||||
"authServer": "https://auth.example.org",
|
"authServer": "https://auth.example.org",
|
||||||
}
|
}
|
||||||
|
|
||||||
The `kid` field serves to distinguish among multiple keys, and must match
|
If multiple keys are provided, then they will all be tried in turn (the
|
||||||
the value provided by the authorisation server. If the server doesn't
|
kid field, if provided, is ignored).
|
||||||
provide a `kid`, the first key with a matching `alg` field will be used.
|
|
||||||
|
|
||||||
If an authorisation server is specified, then the default client, after it
|
If an authorisation server is specified, then the default client, after it
|
||||||
prompts for a password, will request a token from the authorisation server
|
prompts for a password, will request a token from the authorisation server
|
||||||
|
|
27
token/jwt.go
27
token/jwt.go
|
@ -28,7 +28,7 @@ func parseBase64(k string, d map[string]interface{}) ([]byte, error) {
|
||||||
return vv, nil
|
return vv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseKey(key map[string]interface{}) (interface{}, error) {
|
func ParseKey(key map[string]any) (any, error) {
|
||||||
kty, ok := key["kty"].(string)
|
kty, ok := key["kty"].(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("kty not found")
|
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) {
|
func ParseKeys(keys []map[string]any) ([]jwt.VerificationKey, error) {
|
||||||
alg, _ := header["alg"].(string)
|
ks := make([]jwt.VerificationKey, len(keys))
|
||||||
kid, _ := header["kid"].(string)
|
for i, ky := range keys {
|
||||||
for _, k := range keys {
|
k, err := ParseKey(ky)
|
||||||
kid2, _ := k["kid"].(string)
|
if err != nil {
|
||||||
alg2, _ := k["alg"].(string)
|
return nil, err
|
||||||
if (kid == "" || kid == kid2) && alg == alg2 {
|
|
||||||
return ParseKey(k)
|
|
||||||
}
|
}
|
||||||
|
ks[i] = k
|
||||||
}
|
}
|
||||||
return nil, errors.New("key not found")
|
return ks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toStringArray(a interface{}) ([]string, bool) {
|
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.
|
// parseJWT tries to parse a string as a JWT.
|
||||||
// It returns (nil, nil) if the string does not look like 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(
|
t, err := jwt.Parse(
|
||||||
token,
|
token,
|
||||||
func(t *jwt.Token) (interface{}, error) {
|
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.WithExpirationRequired(),
|
||||||
jwt.WithIssuedAt(),
|
jwt.WithIssuedAt(),
|
||||||
|
|
Loading…
Reference in a new issue