2019-05-07 01:20:36 +02:00
package cookies
import (
2019-12-16 20:10:04 +02:00
"fmt"
2019-05-07 01:20:36 +02:00
"net"
"net/http"
"strings"
"time"
2020-03-29 15:54:36 +02:00
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
"github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
2019-05-07 01:20:36 +02:00
)
// MakeCookie constructs a cookie from the given parameters,
// discovering the domain from the request if not specified.
2019-12-16 20:10:04 +02:00
func MakeCookie ( req * http . Request , name string , value string , path string , domain string , httpOnly bool , secure bool , expiration time . Duration , now time . Time , sameSite http . SameSite ) * http . Cookie {
2019-05-07 01:20:36 +02:00
if domain != "" {
host := req . Host
if h , _ , err := net . SplitHostPort ( host ) ; err == nil {
host = h
}
if ! strings . HasSuffix ( host , domain ) {
logger . Printf ( "Warning: request host is %q but using configured cookie domain of %q" , host , domain )
}
}
return & http . Cookie {
Name : name ,
Value : value ,
Path : path ,
Domain : domain ,
HttpOnly : httpOnly ,
Secure : secure ,
Expires : now . Add ( expiration ) ,
2019-12-16 20:10:04 +02:00
SameSite : sameSite ,
2019-05-07 01:20:36 +02:00
}
}
2019-05-13 17:01:28 +02:00
2019-12-20 16:44:59 +02:00
// MakeCookieFromOptions constructs a cookie based on the given *options.CookieOptions,
2019-05-13 17:01:28 +02:00
// value and creation time
2020-04-12 15:00:59 +02:00
func MakeCookieFromOptions ( req * http . Request , name string , value string , cookieOpts * options . CookieOptions , expiration time . Duration , now time . Time ) * http . Cookie {
domain := GetCookieDomain ( req , cookieOpts . Domains )
2020-04-12 13:00:44 +02:00
if domain != "" {
2020-04-12 15:00:59 +02:00
return MakeCookie ( req , name , value , cookieOpts . Path , domain , cookieOpts . HTTPOnly , cookieOpts . Secure , expiration , now , ParseSameSite ( cookieOpts . SameSite ) )
2020-04-12 13:00:44 +02:00
}
// If nothing matches, create the cookie with the shortest domain
2020-04-12 15:00:59 +02:00
logger . Printf ( "Warning: request host %q did not match any of the specific cookie domains of %q" , GetRequestHost ( req ) , strings . Join ( cookieOpts . Domains , "," ) )
2020-04-12 13:00:44 +02:00
defaultDomain := ""
2020-04-12 15:00:59 +02:00
if len ( cookieOpts . Domains ) > 0 {
defaultDomain = cookieOpts . Domains [ len ( cookieOpts . Domains ) - 1 ]
2020-04-12 13:00:44 +02:00
}
2020-04-12 15:00:59 +02:00
return MakeCookie ( req , name , value , cookieOpts . Path , defaultDomain , cookieOpts . HTTPOnly , cookieOpts . Secure , expiration , now , ParseSameSite ( cookieOpts . SameSite ) )
2020-04-12 13:00:44 +02:00
}
// GetCookieDomain returns the correct cookie domain given a list of domains
// by checking the X-Fowarded-Host and host header of an an http request
func GetCookieDomain ( req * http . Request , cookieDomains [ ] string ) string {
host := GetRequestHost ( req )
for _ , domain := range cookieDomains {
if strings . HasSuffix ( host , domain ) {
return domain
}
}
return ""
}
// GetRequestHost return the request host header or X-Forwarded-Host if present
func GetRequestHost ( req * http . Request ) string {
host := req . Header . Get ( "X-Forwarded-Host" )
if host == "" {
host = req . Host
}
return host
2019-12-16 20:10:04 +02:00
}
// Parse a valid http.SameSite value from a user supplied string for use of making cookies.
func ParseSameSite ( v string ) http . SameSite {
switch v {
case "lax" :
return http . SameSiteLaxMode
case "strict" :
return http . SameSiteStrictMode
case "none" :
return http . SameSiteNoneMode
case "" :
return http . SameSiteDefaultMode
default :
panic ( fmt . Sprintf ( "Invalid value for SameSite: %s" , v ) )
}
2019-05-13 17:01:28 +02:00
}