summaryrefslogtreecommitdiffstats
path: root/models/oauth2_application.go
diff options
context:
space:
mode:
authorJohnny Oskarsson <johnny@joskar.se>2021-01-01 17:33:27 +0100
committerGitHub <noreply@github.com>2021-01-02 00:33:27 +0800
commita07e67d9cc39506c149cfadcf0d6f2ab234cf7f6 (patch)
treee4724060cbd74a2374be9bab8c4f13773ee3b270 /models/oauth2_application.go
parent4f2f08bd804a6b900e9e7e7bd7f6e7f073581966 (diff)
downloadgitea-a07e67d9cc39506c149cfadcf0d6f2ab234cf7f6.tar.gz
gitea-a07e67d9cc39506c149cfadcf0d6f2ab234cf7f6.zip
Minimal OpenID Connect implementation (#14139)
This is "minimal" in the sense that only the Authorization Code Flow from OpenID Connect Core is implemented. No discovery, no configuration endpoint, and no user scope management. OpenID Connect is an extension to the (already implemented) OAuth 2.0 protocol, and essentially an `id_token` JWT is added to the access token endpoint response when using the Authorization Code Flow. I also added support for the "nonce" field since it is required to be used in the id_token if the client decides to include it in its initial request. In order to enable this extension an OAuth 2.0 scope containing "openid" is needed. Other OAuth 2.0 requests should not be impacted by this change. This minimal implementation is enough to enable single sign-on (SSO) for other sites, e.g. by using something like `mod_auth_openidc` to only allow access to a CI server if a user has logged into Gitea. Fixes: #1310 Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: zeripath <art27@cantab.net>
Diffstat (limited to 'models/oauth2_application.go')
-rw-r--r--models/oauth2_application.go47
1 files changed, 44 insertions, 3 deletions
diff --git a/models/oauth2_application.go b/models/oauth2_application.go
index af4d280d0c..1b544e4e9e 100644
--- a/models/oauth2_application.go
+++ b/models/oauth2_application.go
@@ -9,6 +9,7 @@ import (
"encoding/base64"
"fmt"
"net/url"
+ "strings"
"time"
"code.gitea.io/gitea/modules/secret"
@@ -103,14 +104,15 @@ func (app *OAuth2Application) getGrantByUserID(e Engine, userID int64) (grant *O
}
// CreateGrant generates a grant for an user
-func (app *OAuth2Application) CreateGrant(userID int64) (*OAuth2Grant, error) {
- return app.createGrant(x, userID)
+func (app *OAuth2Application) CreateGrant(userID int64, scope string) (*OAuth2Grant, error) {
+ return app.createGrant(x, userID, scope)
}
-func (app *OAuth2Application) createGrant(e Engine, userID int64) (*OAuth2Grant, error) {
+func (app *OAuth2Application) createGrant(e Engine, userID int64, scope string) (*OAuth2Grant, error) {
grant := &OAuth2Grant{
ApplicationID: app.ID,
UserID: userID,
+ Scope: scope,
}
_, err := e.Insert(grant)
if err != nil {
@@ -380,6 +382,8 @@ type OAuth2Grant struct {
Application *OAuth2Application `xorm:"-"`
ApplicationID int64 `xorm:"INDEX unique(user_application)"`
Counter int64 `xorm:"NOT NULL DEFAULT 1"`
+ Scope string `xorm:"TEXT"`
+ Nonce string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
@@ -431,6 +435,30 @@ func (grant *OAuth2Grant) increaseCount(e Engine) error {
return nil
}
+// ScopeContains returns true if the grant scope contains the specified scope
+func (grant *OAuth2Grant) ScopeContains(scope string) bool {
+ for _, currentScope := range strings.Split(grant.Scope, " ") {
+ if scope == currentScope {
+ return true
+ }
+ }
+ return false
+}
+
+// SetNonce updates the current nonce value of a grant
+func (grant *OAuth2Grant) SetNonce(nonce string) error {
+ return grant.setNonce(x, nonce)
+}
+
+func (grant *OAuth2Grant) setNonce(e Engine, nonce string) error {
+ grant.Nonce = nonce
+ _, err := e.ID(grant.ID).Cols("nonce").Update(grant)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
// GetOAuth2GrantByID returns the grant with the given ID
func GetOAuth2GrantByID(id int64) (*OAuth2Grant, error) {
return getOAuth2GrantByID(x, id)
@@ -533,3 +561,16 @@ func (token *OAuth2Token) SignToken() (string, error) {
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS512, token)
return jwtToken.SignedString(setting.OAuth2.JWTSecretBytes)
}
+
+// OIDCToken represents an OpenID Connect id_token
+type OIDCToken struct {
+ jwt.StandardClaims
+ Nonce string `json:"nonce,omitempty"`
+}
+
+// SignToken signs an id_token with the (symmetric) client secret key
+func (token *OIDCToken) SignToken(clientSecret string) (string, error) {
+ token.IssuedAt = time.Now().Unix()
+ jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, token)
+ return jwtToken.SignedString([]byte(clientSecret))
+}