1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-01-10 04:18:14 +02:00
oauth2-proxy/pkg/middleware/jwt_session.go

129 lines
4.0 KiB
Go
Raw Normal View History

2020-07-04 20:19:36 +02:00
package middleware
import (
2020-11-26 21:47:44 +02:00
"errors"
2020-07-04 20:19:36 +02:00
"fmt"
"net/http"
"regexp"
"github.com/justinas/alice"
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
2020-11-26 21:47:44 +02:00
k8serrors "k8s.io/apimachinery/pkg/util/errors"
2020-07-04 20:19:36 +02:00
)
const jwtRegexFormat = `^ey[IJ][a-zA-Z0-9_-]*\.ey[IJ][a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]+$`
2020-07-04 20:19:36 +02:00
func NewJwtSessionLoader(sessionLoaders []middlewareapi.TokenToSessionFunc) alice.Constructor {
2020-07-04 20:19:36 +02:00
js := &jwtSessionLoader{
jwtRegex: regexp.MustCompile(jwtRegexFormat),
sessionLoaders: sessionLoaders,
}
return js.loadSession
}
// jwtSessionLoader is responsible for loading sessions from JWTs in
// Authorization headers.
type jwtSessionLoader struct {
jwtRegex *regexp.Regexp
sessionLoaders []middlewareapi.TokenToSessionFunc
2020-07-04 20:19:36 +02:00
}
// loadSession attempts to load a session from a JWT stored in an Authorization
// header within the request.
// If no authorization header is found, or the header is invalid, no session
// will be loaded and the request will be passed to the next handler.
// If a session was loaded by a previous handler, it will not be replaced.
func (j *jwtSessionLoader) loadSession(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
scope := middlewareapi.GetRequestScope(req)
2020-07-04 20:19:36 +02:00
// If scope is nil, this will panic.
// A scope should always be injected before this handler is called.
if scope.Session != nil {
// The session was already loaded, pass to the next handler
next.ServeHTTP(rw, req)
return
}
session, err := j.getJwtSession(req)
if err != nil {
logger.Errorf("Error retrieving session from token in Authorization header: %v", err)
2020-07-04 20:19:36 +02:00
}
// Add the session to the scope if it was found
scope.Session = session
next.ServeHTTP(rw, req)
})
}
// getJwtSession loads a session based on a JWT token in the authorization header.
// (see the config options skip-jwt-bearer-tokens and extra-jwt-issuers)
func (j *jwtSessionLoader) getJwtSession(req *http.Request) (*sessionsapi.SessionState, error) {
auth := req.Header.Get("Authorization")
if auth == "" {
// No auth header provided, so don't attempt to load a session
return nil, nil
}
token, err := j.findTokenFromHeader(auth)
2020-07-04 20:19:36 +02:00
if err != nil {
return nil, err
}
2020-11-26 21:47:44 +02:00
// This leading error message only occurs if all session loaders fail
errs := []error{errors.New("unable to verify bearer token")}
2020-07-04 20:19:36 +02:00
for _, loader := range j.sessionLoaders {
session, err := loader(req.Context(), token)
2020-11-26 21:47:44 +02:00
if err != nil {
errs = append(errs, err)
2020-11-26 21:47:44 +02:00
continue
2020-07-04 20:19:36 +02:00
}
2020-11-26 21:47:44 +02:00
return session, nil
2020-07-04 20:19:36 +02:00
}
2020-11-26 21:47:44 +02:00
return nil, k8serrors.NewAggregate(errs)
2020-07-04 20:19:36 +02:00
}
// findTokenFromHeader finds a valid JWT token from the Authorization header of a given request.
func (j *jwtSessionLoader) findTokenFromHeader(header string) (string, error) {
2020-07-04 20:19:36 +02:00
tokenType, token, err := splitAuthHeader(header)
if err != nil {
return "", err
}
if tokenType == "Bearer" && j.jwtRegex.MatchString(token) {
// Found a JWT as a bearer token
return token, nil
}
if tokenType == "Basic" {
// Check if we have a Bearer token masquerading in Basic
return j.getBasicToken(token)
}
return "", fmt.Errorf("no valid bearer token found in authorization header")
}
// getBasicToken tries to extract a token from the basic value provided.
func (j *jwtSessionLoader) getBasicToken(token string) (string, error) {
user, password, err := getBasicAuthCredentials(token)
2020-07-04 20:19:36 +02:00
if err != nil {
return "", err
2020-07-04 20:19:36 +02:00
}
// check user, user+password, or just password for a token
if j.jwtRegex.MatchString(user) {
// Support blank passwords or magic `x-oauth-basic` passwords - nothing else
/* #nosec G101 */
2020-07-04 20:19:36 +02:00
if password == "" || password == "x-oauth-basic" {
return user, nil
}
} else if j.jwtRegex.MatchString(password) {
// support passwords and ignore user
return password, nil
}
return "", fmt.Errorf("invalid basic auth token found in authorization header")
}