Skip to content

Commit

Permalink
Add asymmetric JWT signing (#16010)
Browse files Browse the repository at this point in the history
* Added asymmetric token signing.

* Load signing key from settings.

* Added optional kid parameter.

* Updated documentation.

* Add "kid" to token header.
  • Loading branch information
KN4CK3R authored Jun 17, 2021
1 parent f7cd394 commit 29695cd
Show file tree
Hide file tree
Showing 13 changed files with 481 additions and 47 deletions.
2 changes: 1 addition & 1 deletion cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func runGenerateInternalToken(c *cli.Context) error {
}

func runGenerateLfsJwtSecret(c *cli.Context) error {
JWTSecretBase64, err := generate.NewJwtSecret()
JWTSecretBase64, err := generate.NewJwtSecretBase64()
if err != nil {
return err
}
Expand Down
4 changes: 3 additions & 1 deletion docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -858,7 +858,9 @@ NB: You must have `DISABLE_ROUTER_LOG` set to `false` for this option to take ef
- `ACCESS_TOKEN_EXPIRATION_TIME`: **3600**: Lifetime of an OAuth2 access token in seconds
- `REFRESH_TOKEN_EXPIRATION_TIME`: **730**: Lifetime of an OAuth2 refresh token in hours
- `INVALIDATE_REFRESH_TOKENS`: **false**: Check if refresh token has already been used
- `JWT_SECRET`: **\<empty\>**: OAuth2 authentication secret for access and refresh tokens, change this a unique string.
- `JWT_SIGNING_ALGORITHM`: **RS256**: Algorithm used to sign OAuth2 tokens. Valid values: \[`HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`\]
- `JWT_SECRET`: **\<empty\>**: OAuth2 authentication secret for access and refresh tokens, change this to a unique string. This setting is only needed if `JWT_SIGNING_ALGORITHM` is set to `HS256`, `HS384` or `HS512`.
- `JWT_SIGNING_PRIVATE_KEY_FILE`: **jwt/private.pem**: Private key file path used to sign OAuth2 tokens. The path is relative to `CUSTOM_PATH`. This setting is only needed if `JWT_SIGNING_ALGORITHM` is set to `RS256`, `RS384`, `RS512`, `ES256`, `ES384` or `ES512`. The file must contain a RSA or ECDSA private key in the PKCS8 format.
- `MAX_TOKEN_LENGTH`: **32767**: Maximum length of token/cookie to accept from OAuth2 provider

## i18n (`i18n`)
Expand Down
11 changes: 7 additions & 4 deletions docs/content/doc/developers/oauth2-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ Gitea supports acting as an OAuth2 provider to allow third party applications to

## Endpoints

| Endpoint | URL |
| ---------------------- | --------------------------- |
| Authorization Endpoint | `/login/oauth/authorize` |
| Access Token Endpoint | `/login/oauth/access_token` |
| Endpoint | URL |
| ------------------------ | ----------------------------------- |
| OpenID Connect Discovery | `/.well-known/openid-configuration` |
| Authorization Endpoint | `/login/oauth/authorize` |
| Access Token Endpoint | `/login/oauth/access_token` |
| OpenID Connect UserInfo | `/login/oauth/userinfo` |
| JSON Web Key Set | `/login/oauth/keys` |

## Supported OAuth2 Grants

Expand Down
3 changes: 3 additions & 0 deletions models/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
return err
}
if err := oauth2.Init(x); err != nil {
return err
}
Expand Down
18 changes: 10 additions & 8 deletions models/oauth2_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/auth/oauth2"
"code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"

Expand Down Expand Up @@ -540,10 +540,10 @@ type OAuth2Token struct {
// ParseOAuth2Token parses a singed jwt string
func ParseOAuth2Token(jwtToken string) (*OAuth2Token, error) {
parsedToken, err := jwt.ParseWithClaims(jwtToken, &OAuth2Token{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
if token.Method == nil || token.Method.Alg() != oauth2.DefaultSigningKey.SigningMethod().Alg() {
return nil, fmt.Errorf("unexpected signing algo: %v", token.Header["alg"])
}
return setting.OAuth2.JWTSecretBytes, nil
return oauth2.DefaultSigningKey.VerifyKey(), nil
})
if err != nil {
return nil, err
Expand All @@ -559,8 +559,9 @@ func ParseOAuth2Token(jwtToken string) (*OAuth2Token, error) {
// SignToken signs the token with the JWT secret
func (token *OAuth2Token) SignToken() (string, error) {
token.IssuedAt = time.Now().Unix()
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS512, token)
return jwtToken.SignedString(setting.OAuth2.JWTSecretBytes)
jwtToken := jwt.NewWithClaims(oauth2.DefaultSigningKey.SigningMethod(), token)
oauth2.DefaultSigningKey.PreProcessToken(jwtToken)
return jwtToken.SignedString(oauth2.DefaultSigningKey.SignKey())
}

// OIDCToken represents an OpenID Connect id_token
Expand All @@ -583,8 +584,9 @@ type OIDCToken struct {
}

// SignToken signs an id_token with the (symmetric) client secret key
func (token *OIDCToken) SignToken(clientSecret string) (string, error) {
func (token *OIDCToken) SignToken(signingKey oauth2.JWTSigningKey) (string, error) {
token.IssuedAt = time.Now().Unix()
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, token)
return jwtToken.SignedString([]byte(clientSecret))
jwtToken := jwt.NewWithClaims(signingKey.SigningMethod(), token)
signingKey.PreProcessToken(jwtToken)
return jwtToken.SignedString(signingKey.SignKey())
}
Loading

0 comments on commit 29695cd

Please sign in to comment.