Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
package jwx
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"strings"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
)
// SignClaims signs the given claims using a given key and a method
func SignClaims(claims jwt.Claims, key interface{}, method jwt.SigningMethod) (string, error) {
token := jwt.NewWithClaims(method, claims)
return token.SignedString(key)
}
// DecodeAccessTokenHeader decodes the header of the accessToken
func DecodeAccessTokenHeader(token string) (*DecodedAccessTokenHeader, error) {
const errMessage = "could not decode access token header"
token = strings.Replace(token, "Bearer ", "", 1)
headerString := strings.Split(token, ".")
decodedData, err := base64.RawStdEncoding.DecodeString(headerString[0])
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
result := &DecodedAccessTokenHeader{}
err = json.Unmarshal(decodedData, result)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
return result, nil
}
func toBigInt(v string) (*big.Int, error) {
decRes, err := base64.RawURLEncoding.DecodeString(v)
if err != nil {
return nil, err
}
res := big.NewInt(0)
res.SetBytes(decRes)
return res, nil
}
var (
curves = map[string]elliptic.Curve{
"P-224": elliptic.P224(),
"P-256": elliptic.P256(),
"P-384": elliptic.P384(),
"P-521": elliptic.P521(),
}
)
func decodeECDSAPublicKey(x, y, crv *string) (*ecdsa.PublicKey, error) {
const errMessage = "could not decode public key"
xInt, err := toBigInt(*x)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
yInt, err := toBigInt(*y)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
var c elliptic.Curve
var ok bool
if c, ok = curves[*crv]; !ok {
return nil, errors.Wrap(fmt.Errorf("unknown curve alg: %s", *crv), errMessage)
}
return &ecdsa.PublicKey{X: xInt, Y: yInt, Curve: c}, nil
}
func decodeRSAPublicKey(e, n *string) (*rsa.PublicKey, error) {
const errMessage = "could not decode public key"
nInt, err := toBigInt(*n)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
decE, err := base64.RawURLEncoding.DecodeString(*e)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
var eBytes []byte
if len(decE) < 8 {
eBytes = make([]byte, 8-len(decE), 8)
eBytes = append(eBytes, decE...)
} else {
eBytes = decE
}
eReader := bytes.NewReader(eBytes)
var eInt uint64
err = binary.Read(eReader, binary.BigEndian, &eInt)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
pKey := rsa.PublicKey{N: nInt, E: int(eInt)}
return &pKey, nil
}
// DecodeAccessTokenRSACustomClaims decodes string access token into jwt.Token
func DecodeAccessTokenRSACustomClaims(accessToken string, e, n *string, customClaims jwt.Claims) (*jwt.Token, error) {
const errMessage = "could not decode accessToken with custom claims"
accessToken = strings.Replace(accessToken, "Bearer ", "", 1)
rsaPublicKey, err := decodeRSAPublicKey(e, n)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
token2, err := jwt.ParseWithClaims(accessToken, customClaims, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return rsaPublicKey, nil
})
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
return token2, nil
}
// DecodeAccessTokenECDSACustomClaims decodes string access token into jwt.Token
func DecodeAccessTokenECDSACustomClaims(accessToken string, x, y, crv *string, customClaims jwt.Claims) (*jwt.Token, error) {
const errMessage = "could not decode accessToken"
accessToken = strings.Replace(accessToken, "Bearer ", "", 1)
publicKey, err := decodeECDSAPublicKey(x, y, crv)
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
token2, err := jwt.ParseWithClaims(accessToken, customClaims, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return publicKey, nil
})
if err != nil {
return nil, errors.Wrap(err, errMessage)
}
return token2, nil
}
+59
View File
@@ -0,0 +1,59 @@
package jwx
import jwt "github.com/golang-jwt/jwt/v5"
// DecodedAccessTokenHeader is the decoded header from the access token
type DecodedAccessTokenHeader struct {
Alg string `json:"alg"`
Typ string `json:"typ"`
Kid string `json:"kid"`
}
// Claims served by keycloak inside the accessToken
type Claims struct {
jwt.RegisteredClaims
Typ string `json:"typ,omitempty"`
Azp string `json:"azp,omitempty"`
AuthTime int `json:"auth_time,omitempty"`
SessionState string `json:"session_state,omitempty"`
Acr string `json:"acr,omitempty"`
AllowedOrigins []string `json:"allowed-origins,omitempty"`
RealmAccess RealmAccess `json:"realm_access,omitempty"`
ResourceAccess ResourceAccess `json:"resource_access,omitempty"`
Scope string `json:"scope,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Address Address `json:"address,omitempty"`
Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
Email string `json:"email,omitempty"`
ClientID string `json:"clientId,omitempty"`
ClientHost string `json:"clientHost,omitempty"`
ClientIP string `json:"clientAddress,omitempty"`
}
// Address TODO what fields does any address have?
type Address struct {
}
// RealmAccess holds roles of the user
type RealmAccess struct {
Roles []string `json:"roles,omitempty"`
}
// ResourceAccess holds TODO: What does it hold?
type ResourceAccess struct {
RealmManagement RealmManagement `json:"realm-management,omitempty"`
Account Account `json:"account,omitempty"`
}
// RealmManagement holds TODO: What does it hold?
type RealmManagement struct {
Roles []string `json:"roles,omitempty"`
}
// Account holds TODO: What does it hold?
type Account struct {
Roles []string `json:"roles,omitempty"`
}