Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add asymmetric JWT signing #16010

Merged
merged 16 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
8 changes: 5 additions & 3 deletions docs/content/doc/advanced/config-cheat-sheet.en-us.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ relation to port exhaustion.
- `ISSUE_INDEXER_PATH`: **indexers/issues.bleve**: Index file used for issue search; available when ISSUE_INDEXER_TYPE is bleve and elasticsearch.
- The next 4 configuration values are deprecated and should be set in `queue.issue_indexer` however are kept for backwards compatibility:
- `ISSUE_INDEXER_QUEUE_TYPE`: **levelqueue**: Issue indexer queue, currently supports:`channel`, `levelqueue`, `redis`.
- `ISSUE_INDEXER_QUEUE_DIR`: **queues/common**: When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this will be the path where the queue will be saved. (Previously this was `indexers/issues.queue`.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per discussion in chat, these changed lines are fine due to the fact that it cleans up line endings (shakes fist at windows)

- `ISSUE_INDEXER_QUEUE_DIR`: **queues/common**: When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this will be the path where the queue will be saved. (Previously this was `indexers/issues.queue`.)
- `ISSUE_INDEXER_QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: When `ISSUE_INDEXER_QUEUE_TYPE` is `redis`, this will store the redis connection string. When `ISSUE_INDEXER_QUEUE_TYPE` is `levelqueue`, this is a directory or additional options of the form `leveldb://path/to/db?option=value&....`, and overrides `ISSUE_INDEXER_QUEUE_DIR`.
- `ISSUE_INDEXER_QUEUE_BATCH_NUMBER`: **20**: Batch queue number.

Expand All @@ -370,7 +370,7 @@ relation to port exhaustion.
## Queue (`queue` and `queue.*`)

- `TYPE`: **persistable-channel**: General queue type, currently support: `persistable-channel` (uses a LevelDB internally), `channel`, `level`, `redis`, `dummy`
- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for individual queues can be set in `queue.name` sections but will default to `DATADIR/`**`common`**. (Previously each queue would default to `DATADIR/`**`name`**.)
- `DATADIR`: **queues/**: Base DataDir for storing persistent and level queues. `DATADIR` for individual queues can be set in `queue.name` sections but will default to `DATADIR/`**`common`**. (Previously each queue would default to `DATADIR/`**`name`**.)
- `LENGTH`: **20**: Maximal queue size before channel queues block
- `BATCH_LENGTH`: **20**: Batch data before passing to the handler
- `CONN_STR`: **redis://127.0.0.1:6379/0**: Connection string for the redis queue type. Options can be set using query params. Similarly LevelDB options can also be set using: **leveldb://relative/path?option=value** or **leveldb:///absolute/path?option=value**, and will override `DATADIR`
Expand Down Expand Up @@ -851,7 +851,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 {
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
return err
}
if err := oauth2.Init(x); err != nil {
return err
}
Expand Down
16 changes: 8 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,8 @@ 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)
return jwtToken.SignedString(oauth2.DefaultSigningKey.SignKey())
techknowlogick marked this conversation as resolved.
Show resolved Hide resolved
}

// OIDCToken represents an OpenID Connect id_token
Expand All @@ -570,8 +570,8 @@ 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)
return jwtToken.SignedString(signingKey.SignKey())
}
Loading