2019-11-23 02:33:31 +03:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2021-06-09 20:53:16 +03:00
package auth
2019-11-23 02:33:31 +03:00
import (
2021-01-05 16:05:40 +03:00
"net/http"
2019-11-23 02:33:31 +03:00
"strings"
"time"
"code.gitea.io/gitea/models"
2022-01-02 16:12:35 +03:00
"code.gitea.io/gitea/models/auth"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2019-11-23 02:33:31 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
2021-01-30 11:55:53 +03:00
"code.gitea.io/gitea/modules/web/middleware"
2021-07-24 13:16:34 +03:00
"code.gitea.io/gitea/services/auth/source/oauth2"
2019-11-23 02:33:31 +03:00
)
// Ensure the struct implements the interface.
var (
2021-07-24 13:16:34 +03:00
_ Method = & OAuth2 { }
_ Named = & OAuth2 { }
2019-11-23 02:33:31 +03:00
)
// CheckOAuthAccessToken returns uid of user from oauth token
func CheckOAuthAccessToken ( accessToken string ) int64 {
// JWT tokens require a "."
if ! strings . Contains ( accessToken , "." ) {
return 0
}
2021-08-27 22:28:00 +03:00
token , err := oauth2 . ParseToken ( accessToken , oauth2 . DefaultSigningKey )
2019-11-23 02:33:31 +03:00
if err != nil {
2021-08-27 22:28:00 +03:00
log . Trace ( "oauth2.ParseToken: %v" , err )
2019-11-23 02:33:31 +03:00
return 0
}
2022-01-02 16:12:35 +03:00
var grant * auth . OAuth2Grant
if grant , err = auth . GetOAuth2GrantByID ( token . GrantID ) ; err != nil || grant == nil {
2019-11-23 02:33:31 +03:00
return 0
}
2021-07-24 13:16:34 +03:00
if token . Type != oauth2 . TypeAccessToken {
2019-11-23 02:33:31 +03:00
return 0
}
2022-01-21 00:52:56 +03:00
if token . ExpiresAt . Before ( time . Now ( ) ) || token . IssuedAt . After ( time . Now ( ) ) {
2019-11-23 02:33:31 +03:00
return 0
}
return grant . UserID
}
2021-06-09 20:53:16 +03:00
// OAuth2 implements the Auth interface and authenticates requests
2019-11-23 02:33:31 +03:00
// (API requests only) by looking for an OAuth token in query parameters or the
// "Authorization" header.
2022-01-20 20:46:10 +03:00
type OAuth2 struct { }
2019-11-23 02:33:31 +03:00
2021-06-09 20:53:16 +03:00
// Name represents the name of auth method
func ( o * OAuth2 ) Name ( ) string {
return "oauth2"
}
2019-11-23 02:33:31 +03:00
// userIDFromToken returns the user id corresponding to the OAuth token.
2021-01-05 16:05:40 +03:00
func ( o * OAuth2 ) userIDFromToken ( req * http . Request , store DataStore ) int64 {
2021-01-26 18:36:53 +03:00
_ = req . ParseForm ( )
2019-11-23 02:33:31 +03:00
// Check access token.
2021-01-05 16:05:40 +03:00
tokenSHA := req . Form . Get ( "token" )
2019-11-23 02:33:31 +03:00
if len ( tokenSHA ) == 0 {
2021-01-05 16:05:40 +03:00
tokenSHA = req . Form . Get ( "access_token" )
2019-11-23 02:33:31 +03:00
}
if len ( tokenSHA ) == 0 {
// Well, check with header again.
2021-01-05 16:05:40 +03:00
auHead := req . Header . Get ( "Authorization" )
2019-11-23 02:33:31 +03:00
if len ( auHead ) > 0 {
auths := strings . Fields ( auHead )
if len ( auths ) == 2 && ( auths [ 0 ] == "token" || strings . ToLower ( auths [ 0 ] ) == "bearer" ) {
tokenSHA = auths [ 1 ]
}
}
}
if len ( tokenSHA ) == 0 {
return 0
}
// Let's see if token is valid.
if strings . Contains ( tokenSHA , "." ) {
uid := CheckOAuthAccessToken ( tokenSHA )
if uid != 0 {
2021-01-05 16:05:40 +03:00
store . GetData ( ) [ "IsApiToken" ] = true
2019-11-23 02:33:31 +03:00
}
return uid
}
t , err := models . GetAccessTokenBySHA ( tokenSHA )
if err != nil {
2020-08-01 17:45:26 +03:00
if ! models . IsErrAccessTokenNotExist ( err ) && ! models . IsErrAccessTokenEmpty ( err ) {
2019-11-23 02:33:31 +03:00
log . Error ( "GetAccessTokenBySHA: %v" , err )
}
return 0
}
t . UpdatedUnix = timeutil . TimeStampNow ( )
if err = models . UpdateAccessToken ( t ) ; err != nil {
log . Error ( "UpdateAccessToken: %v" , err )
}
2021-01-05 16:05:40 +03:00
store . GetData ( ) [ "IsApiToken" ] = true
2019-11-23 02:33:31 +03:00
return t . UID
}
2021-06-09 20:53:16 +03:00
// Verify extracts the user ID from the OAuth token in the query parameters
2019-11-23 02:33:31 +03:00
// or the "Authorization" header and returns the corresponding user object for that ID.
// If verification is successful returns an existing user object.
// Returns nil if verification fails.
2021-11-24 12:49:20 +03:00
func ( o * OAuth2 ) Verify ( req * http . Request , w http . ResponseWriter , store DataStore , sess SessionStore ) * user_model . User {
2021-09-19 14:49:59 +03:00
if ! db . HasEngine {
2019-11-23 02:33:31 +03:00
return nil
}
2021-08-21 05:16:45 +03:00
if ! middleware . IsAPIPath ( req ) && ! isAttachmentDownload ( req ) && ! isAuthenticatedTokenRequest ( req ) {
2019-11-23 02:33:31 +03:00
return nil
}
2021-01-05 16:05:40 +03:00
id := o . userIDFromToken ( req , store )
2019-11-23 02:33:31 +03:00
if id <= 0 {
return nil
}
2021-05-09 19:04:53 +03:00
log . Trace ( "OAuth2 Authorization: Found token for user[%d]" , id )
2019-11-23 02:33:31 +03:00
2021-11-24 12:49:20 +03:00
user , err := user_model . GetUserByID ( id )
2019-11-23 02:33:31 +03:00
if err != nil {
2021-11-24 12:49:20 +03:00
if ! user_model . IsErrUserNotExist ( err ) {
2019-11-23 02:33:31 +03:00
log . Error ( "GetUserByName: %v" , err )
}
return nil
}
2021-05-09 19:04:53 +03:00
log . Trace ( "OAuth2 Authorization: Logged in user %-v" , user )
2019-11-23 02:33:31 +03:00
return user
}
2021-08-21 05:16:45 +03:00
func isAuthenticatedTokenRequest ( req * http . Request ) bool {
switch req . URL . Path {
case "/login/oauth/userinfo" :
fallthrough
case "/login/oauth/introspect" :
return true
}
return false
}