Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
19 changes: 19 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/authorizerdev/authorizer/internal/oauth"
"github.com/authorizerdev/authorizer/internal/rate_limit"
"github.com/authorizerdev/authorizer/internal/server"
"github.com/authorizerdev/authorizer/internal/service"
"github.com/authorizerdev/authorizer/internal/sms"
"github.com/authorizerdev/authorizer/internal/storage"
"github.com/authorizerdev/authorizer/internal/token"
Expand Down Expand Up @@ -530,6 +531,23 @@ func runRoot(c *cobra.Command, args []string) {
StorageProvider: storageProvider,
})

// Transport-agnostic service layer that hosts public-API operations
// (currently SignUp; more migrate over in subsequent phases). GraphQL,
// gRPC, and REST surfaces all delegate to this.
serviceProvider, err := service.New(&rootArgs.config, &service.Dependencies{
Log: &log,
AuditProvider: auditProvider,
EmailProvider: emailProvider,
EventsProvider: eventsProvider,
MemoryStoreProvider: memoryStoreProvider,
SMSProvider: smsProvider,
StorageProvider: storageProvider,
TokenProvider: tokenProvider,
})
if err != nil {
log.Fatal().Err(err).Msg("failed to create service provider")
}

httpProvider, err := http_handlers.New(&rootArgs.config, &http_handlers.Dependencies{
Log: &log,
AuditProvider: auditProvider,
Expand All @@ -543,6 +561,7 @@ func runRoot(c *cobra.Command, args []string) {
OAuthProvider: oauthProvider,
RateLimitProvider: rateLimitProvider,
AuthorizationProvider: authorizationProvider,
ServiceProvider: serviceProvider,
})
if err != nil {
log.Fatal().Err(err).Msg("failed to create http provider")
Expand Down
42 changes: 33 additions & 9 deletions internal/cookie/cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,46 @@ func ParseSameSite(value string) http.SameSite {
}
}

// SetSession sets the session cookie in the response
// SetSession sets the session cookie in the response.
func SetSession(gc *gin.Context, sessionID string, appCookieSecure bool, sameSite http.SameSite) {
secure := appCookieSecure
httpOnly := true
hostname := parsers.GetHost(gc)
for _, c := range BuildSessionCookies(parsers.GetHost(gc), sessionID, appCookieSecure, sameSite) {
gc.SetSameSite(c.SameSite)
gc.SetCookie(c.Name, c.Value, c.MaxAge, c.Path, c.Domain, c.Secure, c.HttpOnly)
}
}

// BuildSessionCookies returns the pair of session cookies (host-scoped and
// domain-scoped) to set on the response. Transport-agnostic so non-gin
// callers (the service layer, gRPC handlers) can produce them as side-effects.
func BuildSessionCookies(hostname, sessionID string, appCookieSecure bool, sameSite http.SameSite) []*http.Cookie {
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)
if domain != "localhost" {
domain = "." + domain
}

gc.SetSameSite(sameSite)
day := 60 * 60 * 24

gc.SetCookie(constants.AppCookieName+"_session", sessionID, day, "/", host, secure, httpOnly)
gc.SetCookie(constants.AppCookieName+"_session_domain", sessionID, day, "/", domain, secure, httpOnly)
return []*http.Cookie{
{
Name: constants.AppCookieName + "_session",
Value: sessionID,
MaxAge: day,
Path: "/",
Domain: host,
Secure: appCookieSecure,
HttpOnly: true,
SameSite: sameSite,
},
{
Name: constants.AppCookieName + "_session_domain",
Value: sessionID,
MaxAge: day,
Path: "/",
Domain: domain,
Secure: appCookieSecure,
HttpOnly: true,
SameSite: sameSite,
},
}
}

// DeleteSession sets session cookies to expire
Expand Down
56 changes: 39 additions & 17 deletions internal/cookie/mfa_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,55 @@ import (
"github.com/authorizerdev/authorizer/internal/parsers"
)

// SetMfaSession sets the mfa session cookie in the response
// SetMfaSession sets the mfa session cookie in the response.
func SetMfaSession(gc *gin.Context, sessionID string, appCookieSecure bool) {
secure := appCookieSecure
httpOnly := true
hostname := parsers.GetHost(gc)
for _, c := range BuildMfaSessionCookies(parsers.GetHost(gc), sessionID, appCookieSecure) {
gc.SetSameSite(c.SameSite)
gc.SetCookie(c.Name, c.Value, c.MaxAge, c.Path, c.Domain, c.Secure, c.HttpOnly)
}
}

// BuildMfaSessionCookies returns the MFA session cookies (host-scoped and
// domain-scoped) to set on the response. Transport-agnostic mirror of
// SetMfaSession.
//
// SameSite policy mirrors the gin path: Lax when insecure (so cross-site UI
// can still complete the flow), None when secure. See the SetMfaSession
// comment for the historical reasoning and the configurability TODO.
func BuildMfaSessionCookies(hostname, sessionID string, appCookieSecure bool) []*http.Cookie {
host, _ := parsers.GetHostParts(hostname)
domain := parsers.GetDomainName(hostname)
if domain != "localhost" {
domain = "." + domain
}

// Since app cookie can come from cross site it becomes important to set this in lax mode when insecure.
// Example person using custom UI on their app domain and making request to authorizer domain.
// For more information check:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
// https://github.com/gin-gonic/gin/blob/master/context.go#L86
// TODO add ability to configure sameSite (none / lax / strict) via config
sameSite := http.SameSiteNoneMode
if !appCookieSecure {
gc.SetSameSite(http.SameSiteLaxMode)
} else {
gc.SetSameSite(http.SameSiteNoneMode)
sameSite = http.SameSiteLaxMode
}
// TODO allow configuring cookie max-age via config
age := 60

gc.SetCookie(constants.MfaCookieName+"_session", sessionID, age, "/", host, secure, httpOnly)
gc.SetCookie(constants.MfaCookieName+"_session_domain", sessionID, age, "/", domain, secure, httpOnly)
return []*http.Cookie{
{
Name: constants.MfaCookieName + "_session",
Value: sessionID,
MaxAge: age,
Path: "/",
Domain: host,
Secure: appCookieSecure,
HttpOnly: true,
SameSite: sameSite,
},
{
Name: constants.MfaCookieName + "_session_domain",
Value: sessionID,
MaxAge: age,
Path: "/",
Domain: domain,
Secure: appCookieSecure,
HttpOnly: true,
SameSite: sameSite,
},
}
}

// DeleteMfaSession deletes the mfa session cookies to expire
Expand Down
4 changes: 4 additions & 0 deletions internal/graphql/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/authorizerdev/authorizer/internal/events"
"github.com/authorizerdev/authorizer/internal/graph/model"
"github.com/authorizerdev/authorizer/internal/memory_store"
"github.com/authorizerdev/authorizer/internal/service"
"github.com/authorizerdev/authorizer/internal/sms"
"github.com/authorizerdev/authorizer/internal/storage"
"github.com/authorizerdev/authorizer/internal/token"
Expand Down Expand Up @@ -41,6 +42,9 @@ type Dependencies struct {
TokenProvider token.Provider
// AuthorizationProvider is used for fine-grained authorization checks
AuthorizationProvider authorization.Provider
// ServiceProvider hosts the transport-agnostic public-API operations.
// Resolvers for migrated ops (currently just SignUp) delegate here.
ServiceProvider service.Provider
}

// New constructs a new graphql provider with given arguments
Expand Down
Loading
Loading