diff --git a/pkg/server/api/authn/cookie.go b/pkg/server/api/authn/cookie.go new file mode 100644 index 000000000..464646edd --- /dev/null +++ b/pkg/server/api/authn/cookie.go @@ -0,0 +1,52 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package authn + +import ( + "net/http" + "time" + + "go.probo.inc/probo/pkg/coredata" + "go.probo.inc/probo/pkg/securecookie" +) + +type Cookie struct { + config *securecookie.Config +} + +func (c *Cookie) Set(w http.ResponseWriter, session *coredata.Session) { + securecookie.Set( + w, + c.sessionCookieConfig(time.Until(session.ExpiredAt)), + session.ID.String(), + ) +} + +func NewCookie(config *securecookie.Config) *Cookie { + return &Cookie{config} +} + +func (c *Cookie) sessionCookieConfig(maxAge time.Duration) securecookie.Config { + return securecookie.Config{ + Name: c.config.Name, + Secret: c.config.Secret, + Secure: c.config.Secure, + HTTPOnly: c.config.HTTPOnly, + SameSite: c.config.SameSite, + Path: c.config.Path, + Domain: c.config.Domain, + MaxAge: int(maxAge.Seconds()), + } +} diff --git a/pkg/server/api/connect/v1/graphql_handler.go b/pkg/server/api/connect/v1/graphql_handler.go index 6f0788ccd..46845fba5 100644 --- a/pkg/server/api/connect/v1/graphql_handler.go +++ b/pkg/server/api/connect/v1/graphql_handler.go @@ -58,11 +58,11 @@ func SessionDirective(ctx context.Context, obj any, next graphql.Resolver, requi func NewGraphQLHandler(svc *iam.Service, logger *log.Logger, baseURL *baseurl.BaseURL, cookieConfig securecookie.Config) http.Handler { config := schema.Config{ Resolvers: &Resolver{ - authorize: authz.NewAuthorizeFunc(svc, logger), - logger: logger, - iam: svc, - baseURL: baseURL, - cookieConfig: cookieConfig, + authorize: authz.NewAuthorizeFunc(svc, logger), + logger: logger, + iam: svc, + baseURL: baseURL, + sessionCookie: authn.NewCookie(&cookieConfig), }, Directives: schema.DirectiveRoot{ Session: SessionDirective, diff --git a/pkg/server/api/connect/v1/resolver.go b/pkg/server/api/connect/v1/resolver.go index e74215b56..1ac11fa95 100644 --- a/pkg/server/api/connect/v1/resolver.go +++ b/pkg/server/api/connect/v1/resolver.go @@ -19,7 +19,6 @@ package connect_v1 import ( "context" "net/http" - "time" "github.com/go-chi/chi/v5" "go.gearno.de/kit/log" @@ -34,27 +33,14 @@ import ( type ( Resolver struct { - authorize authz.AuthorizeFunc - logger *log.Logger - iam *iam.Service - baseURL *baseurl.BaseURL - cookieConfig securecookie.Config + authorize authz.AuthorizeFunc + logger *log.Logger + iam *iam.Service + baseURL *baseurl.BaseURL + sessionCookie *authn.Cookie } ) -func (r *Resolver) sessionCookieConfig(maxAge time.Duration) securecookie.Config { - return securecookie.Config{ - Name: r.cookieConfig.Name, - Secret: r.cookieConfig.Secret, - Secure: r.cookieConfig.Secure, - HTTPOnly: r.cookieConfig.HTTPOnly, - SameSite: r.cookieConfig.SameSite, - Path: r.cookieConfig.Path, - Domain: r.cookieConfig.Domain, - MaxAge: int(maxAge.Seconds()), - } -} - func NewMux(logger *log.Logger, svc *iam.Service, cookieConfig securecookie.Config, tokenSecret string, baseURL *baseurl.BaseURL) *chi.Mux { r := chi.NewMux() diff --git a/pkg/server/api/connect/v1/v1_resolver.go b/pkg/server/api/connect/v1/v1_resolver.go index a51fb46a1..b034a5a27 100644 --- a/pkg/server/api/connect/v1/v1_resolver.go +++ b/pkg/server/api/connect/v1/v1_resolver.go @@ -19,7 +19,6 @@ import ( "go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/mail" "go.probo.inc/probo/pkg/page" - "go.probo.inc/probo/pkg/securecookie" "go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/authz" "go.probo.inc/probo/pkg/server/api/connect/v1/schema" @@ -382,11 +381,7 @@ func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput) } w := gqlutils.HTTPResponseWriterFromContext(ctx) - securecookie.Set( - w, - r.sessionCookieConfig(time.Until(session.ExpiredAt)), - session.ID.String(), - ) + r.sessionCookie.Set(w, session) return &types.SignInPayload{ Identity: types.NewIdentity(user), @@ -415,11 +410,7 @@ func (r *mutationResolver) SignUp(ctx context.Context, input types.SignUpInput) } w := gqlutils.HTTPResponseWriterFromContext(ctx) - securecookie.Set( - w, - r.sessionCookieConfig(time.Until(session.ExpiredAt)), - session.ID.String(), - ) + r.sessionCookie.Set(w, session) return &types.SignUpPayload{ Identity: types.NewIdentity(identity), @@ -481,11 +472,7 @@ func (r *mutationResolver) SignUpFromInvitation(ctx context.Context, input types } w := gqlutils.HTTPResponseWriterFromContext(ctx) - securecookie.Set( - w, - r.sessionCookieConfig(time.Until(session.ExpiredAt)), - session.ID.String(), - ) + r.sessionCookie.Set(w, session) return &types.SignUpFromInvitationPayload{ Identity: &types.Identity{