feat: add more crypto algorithm for jwt signing (#3150)

* feat: add more algorithm support for JWT signing

* feat: add i18n support

* feat: add i18n support

* feat: optimize if statement

* fix: remove additional space line
This commit is contained in:
DacongDA 2024-08-30 16:59:41 +08:00 committed by GitHub
parent c08f2b1f3f
commit 1adb172d6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 119 additions and 18 deletions

View File

@ -97,6 +97,7 @@ type Application struct {
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
ExpireInHours int `json:"expireInHours"`
RefreshExpireInHours int `json:"refreshExpireInHours"`

View File

@ -112,7 +112,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"},
GrantTypesSupported: []string{"password", "authorization_code"},
SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256"},
IdTokenSigningAlgValuesSupported: []string{"RS256", "RS512", "ES256", "ES384", "ES512"},
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
RequestParameterSupported: true,

View File

@ -17,6 +17,7 @@ package object
import (
"fmt"
"reflect"
"strings"
"time"
"github.com/casdoor/casdoor/util"
@ -378,36 +379,52 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
application.TokenFormat = "JWT"
}
var jwtMethod jwt.SigningMethod
if application.TokenSigningMethod == "RS256" {
jwtMethod = jwt.SigningMethodRS256
} else if application.TokenSigningMethod == "RS512" {
jwtMethod = jwt.SigningMethodRS512
} else if application.TokenSigningMethod == "ES256" {
jwtMethod = jwt.SigningMethodES256
} else if application.TokenSigningMethod == "ES512" {
jwtMethod = jwt.SigningMethodES512
} else if application.TokenSigningMethod == "ES384" {
jwtMethod = jwt.SigningMethodES384
} else {
jwtMethod = jwt.SigningMethodRS256
}
// the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name
if application.TokenFormat == "JWT" {
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
token = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp)
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsWithoutThirdIdp.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
refreshToken = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp)
} else if application.TokenFormat == "JWT-Empty" {
claimsShort := getShortClaims(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
token = jwt.NewWithClaims(jwtMethod, claimsShort)
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
refreshToken = jwt.NewWithClaims(jwtMethod, claimsShort)
} else if application.TokenFormat == "JWT-Custom" {
claimsCustom := getClaimsCustom(claims, application.TokenFields)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsCustom)
token = jwt.NewWithClaims(jwtMethod, claimsCustom)
refreshClaims := getClaimsCustom(claims, application.TokenFields)
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
refreshClaims["TokenType"] = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, refreshClaims)
refreshToken = jwt.NewWithClaims(jwtMethod, refreshClaims)
} else if application.TokenFormat == "JWT-Standard" {
claimsStandard := getStandardClaims(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
token = jwt.NewWithClaims(jwtMethod, claimsStandard)
claimsStandard.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsStandard.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
refreshToken = jwt.NewWithClaims(jwtMethod, claimsStandard)
} else {
return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat)
}
@ -425,34 +442,57 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
}
}
// RSA private key
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
var (
tokenString string
refreshTokenString string
key interface{}
)
if strings.Contains(application.TokenSigningMethod, "RS") || application.TokenSigningMethod == "" {
// RSA private key
key, err = jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
} else if strings.Contains(application.TokenSigningMethod, "ES") {
// ES private key
key, err = jwt.ParseECPrivateKeyFromPEM([]byte(cert.PrivateKey))
} else if strings.Contains(application.TokenSigningMethod, "Ed") {
// Ed private key
key, err = jwt.ParseEdPrivateKeyFromPEM([]byte(cert.PrivateKey))
}
if err != nil {
return "", "", "", err
}
token.Header["kid"] = cert.Name
tokenString, err := token.SignedString(key)
tokenString, err = token.SignedString(key)
if err != nil {
return "", "", "", err
}
refreshTokenString, err := refreshToken.SignedString(key)
refreshTokenString, err = refreshToken.SignedString(key)
return tokenString, refreshTokenString, name, err
}
func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
var (
certificate interface{}
err error
)
if cert.Certificate == "" {
return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert)
}
// RSA certificate
certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
if _, ok := token.Method.(*jwt.SigningMethodRSA); ok {
// RSA certificate
certificate, err = jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
} else if _, ok := token.Method.(*jwt.SigningMethodECDSA); ok {
// ES certificate
certificate, err = jwt.ParseECPublicKeyFromPEM([]byte(cert.Certificate))
} else {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
if err != nil {
return nil, err
}

View File

@ -407,6 +407,16 @@ class ApplicationEditPage extends React.Component {
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token signing method"), i18next.t("application:Token signing method - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenSigningMethod === "" ? "RS256" : this.state.application.tokenSigningMethod} onChange={(value => {this.updateApplicationField("tokenSigningMethod", value);})}
options={["RS256", "RS512", "ES256", "ES512", "ES384"].map((item) => Setting.getOption(item, item))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token fields"), i18next.t("application:Token fields - Tooltip"))} :

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Uživatelská pole zahrnutá v tokenu",
"Token format": "Formát tokenu",
"Token format - Tooltip": "Formát přístupového tokenu",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Nečekali jste, že uvidíte tuto výzvu"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token-Format",
"Token format - Tooltip": "Das Format des Access-Tokens",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Sie sind unerwartet auf diese Aufforderungsseite gelangt"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "The user fields included in the token",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Formato del token",
"Token format - Tooltip": "El formato del token de acceso",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Es inesperado ver esta página de inicio"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Format de jeton",
"Token format - Tooltip": "Le format du jeton d'accès",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Il n'était pas prévu que vous voyez cette page de saisie"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Format token",
"Token format - Tooltip": "Format dari token akses",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Anda tidak mengharapkan untuk melihat halaman prompt ini"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "トークン形式",
"Token format - Tooltip": "アクセストークンのフォーマット",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "このプロンプトページを見ることは予期せぬことである"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "토큰 형식",
"Token format - Tooltip": "접근 토큰의 형식",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "당신은 이 프롬프트 페이지를 볼 것을 예상하지 못했습니다"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Formato do token",
"Token format - Tooltip": "O formato do token de acesso",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Você não deveria ver esta página de prompt"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Формат жетона",
"Token format - Tooltip": "Формат токена доступа",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Вы не ожидали увидеть эту страницу-подсказку"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Používateľské polia zahrnuté v tokene",
"Token format": "Formát tokenu",
"Token format - Tooltip": "Formát prístupového tokenu",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Neočekávali ste, že uvidíte túto výzvu"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Поля маркерів підказка",
"Token format": "Формат маркера",
"Token format - Tooltip": "Формат маркера доступу",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Ви неочікувано побачите цю сторінку запиту"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Định dạng mã thông báo",
"Token format - Tooltip": "Định dạng của mã thông báo truy cập",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Bạn không mong đợi thấy trang này hiện lên"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token中所包含的用户字段",
"Token format": "Access Token格式",
"Token format - Tooltip": "Access Token格式",
"Token signing method": "Token签名算法",
"Token signing method - Tooltip": "JWT token的签名算法需要与证书算法相匹配",
"You are unexpected to see this prompt page": "错误:该提醒页面不应出现"
},
"cert": {