github.com/google/osv-scalibr@v0.4.1/veles/secrets/common/jwt/jwt.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package jwt provides utilities for parsing JSON Web Tokens (JWT). 16 package jwt 17 18 import ( 19 "encoding/base64" 20 "encoding/json" 21 "maps" 22 "regexp" 23 "strings" 24 ) 25 26 // MaxTokenLength defines the maximum allowed size of a JWT token. 27 // 28 // The JWT specification (RFC 7519) does not define an upper bound for token 29 // length. However, in practice JWTs are typically transmitted in HTTP headers, 30 // where very large values can cause interoperability issues. Exceeding 8 KB is 31 // generally discouraged, as many servers, proxies, and libraries impose limits 32 // around this size. 33 const MaxTokenLength = 8192 34 35 // jwtRe is a regular expression that matches the basic JWT structure (base64.base64.base64) 36 var jwtRe = regexp.MustCompile(`eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+`) 37 38 // Token represents a decoded JSON Web Token (JWT). 39 // The JWT consists of three sections: header, payload, and signature. 40 type Token struct { 41 // raw is the original JWT string 42 raw string 43 // header is the base64 decoded JWT header claims. 44 header map[string]any 45 // payload is the base64 decoded JWT payload claims. 46 payload map[string]any 47 // signature is the raw signature section of the JWT. 48 signature string 49 } 50 51 // Header returns a copy of the JWT header claims. 52 func (t *Token) Header() map[string]any { 53 return maps.Clone(t.header) 54 } 55 56 // Payload returns a copy of the JWT payload claims. 57 func (t *Token) Payload() map[string]any { 58 return maps.Clone(t.payload) 59 } 60 61 // Signature returns the JWT signature. 62 func (t *Token) Signature() string { 63 return t.signature 64 } 65 66 // Raw returns the JWT string. 67 func (t *Token) Raw() string { 68 return t.raw 69 } 70 71 func (t Token) isValid() bool { 72 return t.header != nil && t.payload != nil && t.signature != "" 73 } 74 75 // ExtractTokens scans the input data for JWT substrings, parses them and 76 // returns a slice of Token objects and their positions. 77 func ExtractTokens(data []byte) ([]Token, []int) { 78 var tokens []Token 79 var positions []int 80 jwtMatches := jwtRe.FindAllIndex(data, -1) 81 for _, m := range jwtMatches { 82 token := parseToken(string(data[m[0]:m[1]])) 83 if !token.isValid() { 84 continue 85 } 86 tokens = append(tokens, token) 87 positions = append(positions, m[0]) 88 } 89 return tokens, positions 90 } 91 92 // parseToken splits and decode a JWT string into a Token. 93 func parseToken(token string) Token { 94 sections := strings.Split(token, ".") 95 if len(sections) != 3 { 96 return Token{} 97 } 98 99 return Token{ 100 header: extractClaims(sections[0]), 101 payload: extractClaims(sections[1]), 102 signature: sections[2], 103 raw: token, 104 } 105 } 106 107 // extractClaims base64 decodes a JWT section and unmarshals it as JSON. 108 func extractClaims(section string) map[string]any { 109 data, err := base64.RawURLEncoding.DecodeString(section) 110 if err != nil { 111 return nil 112 } 113 114 var claims map[string]any 115 if err := json.Unmarshal(data, &claims); err != nil { 116 return nil 117 } 118 return claims 119 }