Add asymmetric JWT signing #16010
No reviewers
Labels
No Label
backport/done
backport/v1.0
backport/v1.1
backport/v1.10
backport/v1.11
backport/v1.12
backport/v1.13
backport/v1.14
backport/v1.15
backport/v1.2
backport/v1.3
backport/v1.4
backport/v1.5
backport/v1.6
backport/v1.7
backport/v1.8
backport/v1.9
bounty
changelog
dependencies
frontport/done
frontport/main
good first issue
Hacktoberfest
hacktoberfest-accepted
in progress
kind/api
kind/breaking
kind/bug
kind/build
kind/deployment
kind/deprecated
kind/docs
kind/enhancement
kind/feature
kind/lint
kind/misc
kind/moderation
kind/package
kind/proposal
kind/question
kind/refactor
kind/regression
kind/security
kind/summary
kind/testing
kind/translation
kind/ui
kind/upstream-related
kind/usability
kind/ux
lgtm/done
lgtm/need 1
lgtm/need 2
performance/bigrepo
performance/cpu
performance/memory
performance/speed
priority/critical
priority/low
priority/maybe
priority/medium
proposal/rejected
reviewed/confirmed
reviewed/duplicate
reviewed/fixed
reviewed/invalid
reviewed/not-a-bug
reviewed/wontfix
skip-changelog
stale
status/blocked
status/needs-feedback
status/wip
theme/2fa
theme/authentication
theme/avatar
theme/backup-restore
theme/docker
theme/federation
theme/issues
theme/kanban
theme/markdown
theme/migration
theme/mobile
theme/pr
theme/signing
theme/sqlite
theme/timetracker
theme/webhook
theme/wiki
No Milestone
No project
No Assignees
2 Participants
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: lunny/gitea#16010
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature-jwt-asymmetric"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Close #15912
Added asymmetric JWT signing. Supports
HS256
(already implemented),HS384
,HS512
,RS256
,RS384
,RS512
,ES256
,ES384
andES512
.The default signing algorithm is changed from
HS256
(symmetric encryption) toRS256
(asymmetric encryption).⚠️ BREAKING ⚠️
JWT_SIGNING_PRIVATE_KEY_FILE
(by defaultAPP_DATA_PATH/jwt
), a pair will be generated if it is not present. (NB: this was originally inCUSTOM_PATH
but was changed by #16227)JWT_SECRET
will only be used ifJWT_SIGNING_ALGORITHM
is set toHS256
(previous default),HS384
orHS512
.JWT_SIGNING_ALGORITHM
back toHS256
.or see: https://github.com/go-gitea/gitea/pull/16010#issuecomment-879381265
@ -132,6 +132,9 @@ func GetActiveOAuth2Providers() ([]string, map[string]OAuth2Provider, error) {
// InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library
func InitOAuth2() error {
if err := oauth2.InitSigningKey(); err != nil {
Merge it into oauth2.Init is better?
@ -132,6 +132,9 @@ func GetActiveOAuth2Providers() ([]string, map[string]OAuth2Provider, error) {
// InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library
func InitOAuth2() error {
if err := oauth2.InitSigningKey(); err != nil {
I did not add it in the existing method because
oauth2.Init
is used for the other direction (Gitea -> other service). So I thought both methods should be logical separated.per discussion in chat, these changed lines are fine due to the fact that it cleans up line endings (shakes fist at windows)
Thank you so much so far on your work on this PR!!
I was able to sign into Sourcegraph using this PR, however testing this PR more I found that 1. sourcegraph may not be using the cert to validate the signature on this token, 2. possibly the upstream jwt library may not be signing the token correctly.
@ -587,3 +588,2 @@
token.IssuedAt = time.Now().Unix()
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, token)
return jwtToken.SignedString([]byte(clientSecret))
jwtToken := jwt.NewWithClaims(signingKey.SigningMethod(), token)
Attempting to verify the generated token using JWT.io, it is unable to parse the signature. It works w/ HS256, but not RS256. I suspect it is something upstream in github.com/dgrijalva/jwt-go that is signing it differently than other libraries expect. I've tried to dig down into the code changes from this PR, but it looks like everything you are setting is correct.
Please let me know if you require any additional information from me to replicate.
Appendix:
You can also see at https://replit.com/@techknowlogick/BruisedCompetitiveLightweightprocess attempting to parse the key, that in headers it is unable to return KeyID (using smallstep CLI to fetch example token from google
step oauth --oidc --bare
, you can see that KeyID is populated).Sample RS256 token:
@ -587,3 +588,2 @@
token.IssuedAt = time.Now().Unix()
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, token)
return jwtToken.SignedString([]byte(clientSecret))
jwtToken := jwt.NewWithClaims(signingKey.SigningMethod(), token)
JWT.io works for me without changes. Did you change the "Algorithm" on top?
I used this container to get the token:
I then used
openssl rsa -in private.pem -out rsa_priv.pem
and
openssl rsa -in rsa_priv.pem -pubout > rsa_pub.pem
to get the private and public key in the desired format.
@ -0,0 +296,4 @@
func loadOrCreateSymmetricKey() (interface{}, error) {
key := make([]byte, 32)
n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64))
if err != nil || n != 32 {
Currently only 32 bytes are allowed. Should we really enforce this? Smaller keys are valid and larger keys are hashed by the internal hashing algorithm to create a secret with the correct length.
2ebe77a2fd/src/crypto/hmac/hmac.go (L148-L152)
This is the first instance, but there are lots of misspellings of "signing".
should we realy drop this?
It's not dropped. You can find it in
loadOrCreateSymmetricKey
so it's created on demand ... I would prefer if it get added on first gitea start ...
It gets added on first gitea start if you use
HSxxx
as signing algorithm. If you use another algorithm it is not used at all.@ -380,2 +381,4 @@
RefreshTokenExpirationTime: 730,
InvalidateRefreshTokens: false,
JWTSigningAlgorithm: "RS256",
JWTSigningPrivateKeyFile: "jwt/private.pem",
this change default alg to async - not sure if it is breaking ...
@ -380,2 +381,4 @@
RefreshTokenExpirationTime: 730,
InvalidateRefreshTokens: false,
JWTSigningAlgorithm: "RS256",
JWTSigningPrivateKeyFile: "jwt/private.pem",
@ -380,2 +381,4 @@
RefreshTokenExpirationTime: 730,
InvalidateRefreshTokens: false,
JWTSigningAlgorithm: "RS256",
JWTSigningPrivateKeyFile: "jwt/private.pem",
as per KN4CK3R ...
https://accounts.google.com/.well-known/openid-configuration
and other big tech have RS256 - so go for it@ -380,2 +381,4 @@
RefreshTokenExpirationTime: 730,
InvalidateRefreshTokens: false,
JWTSigningAlgorithm: "RS256",
JWTSigningPrivateKeyFile: "jwt/private.pem",
For clarification:
RS256 is defined as "create the signature with SHA256 and use the provided RSA key as secret". The RSA key is allowed to have any size and not just 256 bit. If Gitea generates a key the default size is 4096 bit.