Rename redirect-path to continue + handle redirection on forbidden from wrong org assume

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-02-11 21:19:15 +04:00
parent 942b6f5144
commit e46c1d4894
15 changed files with 74 additions and 55 deletions

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<8717e91a7e1602c06f6c67fa76580ecf>> * @generated SignedSource<<d0c32c536987c19da427a6088151bd09>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,8 +11,8 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type ReauthenticationReason = "POLICY_REQUIREMENT" | "SENSITIVE_ACTION" | "SESSION_EXPIRED"; export type ReauthenticationReason = "POLICY_REQUIREMENT" | "SENSITIVE_ACTION" | "SESSION_EXPIRED";
export type AssumeOrganizationSessionInput = { export type AssumeOrganizationSessionInput = {
continue: string;
organizationId: string; organizationId: string;
redirectPath: string;
}; };
export type AssumePageMutation$variables = { export type AssumePageMutation$variables = {
input: AssumeOrganizationSessionInput; input: AssumeOrganizationSessionInput;

View File

@@ -11,9 +11,12 @@ export function OrganizationErrorBoundary() {
const search = new URLSearchParams([ const search = new URLSearchParams([
["organization-id", organizationId], ["organization-id", organizationId],
["redirect-path", window.location.pathname + window.location.search],
]); ]);
if (window.location.href !== window.location.origin) {
search.set("continue", window.location.href);
}
if (error instanceof UnAuthenticatedError) { if (error instanceof UnAuthenticatedError) {
return <Navigate to={{ pathname: "/auth/login", search: "?" + search.toString() }} />; return <Navigate to={{ pathname: "/auth/login", search: "?" + search.toString() }} />;
} }

View File

@@ -6,12 +6,19 @@ import { PageError } from "./PageError";
export function RootErrorBoundary() { export function RootErrorBoundary() {
const error = useRouteError(); const error = useRouteError();
const search = new URLSearchParams([ const search = new URLSearchParams();
["redirect-path", window.location.pathname + window.location.search], if (window.location.href !== window.location.origin) {
]); search.set("continue", window.location.href);
}
if (error instanceof UnAuthenticatedError) { if (error instanceof UnAuthenticatedError) {
return <Navigate to={{ pathname: "/auth/login", search: "?" + search.toString() }} />; return (
<Navigate to={{
pathname: "/auth/login",
search: search.toString() ? "?" + search.toString() : "",
}}
/>
);
} }
return <PageError error={error instanceof Error ? error : new Error("unknown error")} />; return <PageError error={error instanceof Error ? error : new Error("unknown error")} />;

View File

@@ -3,7 +3,7 @@ import { useTranslate } from "@probo/i18n";
import { Button, Field, IconChevronLeft, useToast } from "@probo/ui"; import { Button, Field, IconChevronLeft, useToast } from "@probo/ui";
import type { FormEventHandler } from "react"; import type { FormEventHandler } from "react";
import { useMutation } from "react-relay"; import { useMutation } from "react-relay";
import { Link, useLocation, useNavigate, useSearchParams } from "react-router"; import { Link, useLocation, useSearchParams } from "react-router";
import { graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
import type { PasswordSignInPageMutation } from "#/__generated__/iam/PasswordSignInPageMutation.graphql"; import type { PasswordSignInPageMutation } from "#/__generated__/iam/PasswordSignInPageMutation.graphql";
@@ -21,7 +21,6 @@ const signInMutation = graphql`
export default function PasswordSignInPage() { export default function PasswordSignInPage() {
const location = useLocation(); const location = useLocation();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const navigate = useNavigate();
const { __ } = useTranslate(); const { __ } = useTranslate();
const { toast } = useToast(); const { toast } = useToast();
@@ -56,10 +55,11 @@ export default function PasswordSignInPage() {
), ),
variant: "error", variant: "error",
}); });
window.location.href = "/";
return; return;
} }
void navigate(searchParams.get("redirect-path") ?? "/"); window.location.href = searchParams.get("continue") ?? window.location.origin;
}, },
onError: (e) => { onError: (e) => {
toast({ toast({
@@ -67,6 +67,7 @@ export default function PasswordSignInPage() {
description: e.message, description: e.message,
variant: "error", variant: "error",
}); });
window.location.href = "/";
}, },
}); });
}; };

View File

@@ -130,7 +130,7 @@ function NavigateToSSOLoginURL(props: {
} }
const url = new URL(ssoLoginURL.value); const url = new URL(ssoLoginURL.value);
url.search = "?" + searchParams.toString(); url.search = searchParams.toString();
window.location.href = url.toString(); window.location.href = url.toString();
}, [__, navigate, ssoLoginURL, toast, searchParams]); }, [__, navigate, ssoLoginURL, toast, searchParams]);

View File

@@ -38,18 +38,18 @@ function AssumePageInner() {
const [assumeOrganizationSession] = useMutation<AssumePageMutation>(assumeMutation); const [assumeOrganizationSession] = useMutation<AssumePageMutation>(assumeMutation);
const redirectPath = searchParams.get("redirect-path") ?? `/organizations/${organizationId}`; const continueUrl = searchParams.get("continue") ?? new URL(`/organizations/${organizationId}`, window.location.origin).toString();
useEffect(() => { useEffect(() => {
assumeOrganizationSession({ assumeOrganizationSession({
variables: { variables: {
input: { organizationId, redirectPath }, input: { organizationId, continue: continueUrl },
}, },
onError: (error) => { onError: (error) => {
if (error instanceof UnAuthenticatedError) { if (error instanceof UnAuthenticatedError) {
const search = new URLSearchParams([ const search = new URLSearchParams([
["organization-id", organizationId], ["organization-id", organizationId],
["redirect-path", redirectPath], ["continue", continueUrl],
]); ]);
void navigate({ pathname: "/auth/login", search: "?" + search.toString() }); void navigate({ pathname: "/auth/login", search: "?" + search.toString() });
@@ -68,7 +68,7 @@ function AssumePageInner() {
switch (result.__typename) { switch (result.__typename) {
case "PasswordRequired": case "PasswordRequired":
search.set("organization-id", organizationId); search.set("organization-id", organizationId);
search.set("redirect-path", redirectPath); search.set("continue", continueUrl);
void navigate({ pathname: "/auth/password-login", search: "?" + search.toString() }); void navigate({ pathname: "/auth/password-login", search: "?" + search.toString() });
break; break;
@@ -79,11 +79,11 @@ function AssumePageInner() {
window.location.href = samlSSOLoginURL.toString(); window.location.href = samlSSOLoginURL.toString();
break; break;
default: default:
void navigate(redirectPath); window.location.href = continueUrl;
} }
}, },
}); });
}, [organizationId, navigate, assumeOrganizationSession, redirectPath, searchParams]); }, [organizationId, navigate, assumeOrganizationSession, continueUrl, searchParams]);
return ( return (
<AuthLayout> <AuthLayout>

View File

@@ -339,7 +339,7 @@ func (c *Client) assumeOrganizationSession() {
err := c.ExecuteConnect(query, map[string]any{ err := c.ExecuteConnect(query, map[string]any{
"input": map[string]any{ "input": map[string]any{
"organizationId": c.organizationID.String(), "organizationId": c.organizationID.String(),
"redirectPath": "/", "continue": c.baseURL,
}, },
}, nil) }, nil)
require.NoError(c.T, err, "assumeOrganizationSession mutation failed") require.NoError(c.T, err, "assumeOrganizationSession mutation failed")

View File

@@ -20,7 +20,7 @@ type (
var ( var (
Nil = GID{} Nil = GID{}
EncodedGIDSize = base64.RawURLEncoding.EncodedLen(GIDSize) EncodedGIDSize = base64.RawURLEncoding.EncodedLen(len(Nil))
) )
// ParseGID parses a string representation of a GID // ParseGID parses a string representation of a GID

View File

@@ -107,7 +107,7 @@ func (s *Service) GenerateSpMetadata() ([]byte, error) {
func (s *Service) InitiateLogin( func (s *Service) InitiateLogin(
ctx context.Context, ctx context.Context,
configID gid.GID, configID gid.GID,
redirectPath string, continuePath string,
) (*url.URL, error) { ) (*url.URL, error) {
var ( var (
now = time.Now() now = time.Now()
@@ -153,7 +153,7 @@ func (s *Service) InitiateLogin(
return fmt.Errorf("cannot insert SAML request: %w", err) return fmt.Errorf("cannot insert SAML request: %w", err)
} }
relayState := config.ID.String() + redirectPath relayState := config.ID.String() + url.QueryEscape(continuePath)
redirect, err = req.Redirect(relayState, sp) redirect, err = req.Redirect(relayState, sp)
if err != nil { if err != nil {

View File

@@ -491,7 +491,7 @@ func (s SessionService) AssumeOrganizationSession(
ctx context.Context, ctx context.Context,
sessionID gid.GID, sessionID gid.GID,
organizationID gid.GID, organizationID gid.GID,
redirectPath string, continueURL string,
) (*coredata.Session, *coredata.Membership, error) { ) (*coredata.Session, *coredata.Membership, error) {
var ( var (
now = time.Now() now = time.Now()
@@ -549,7 +549,7 @@ func (s SessionService) AssumeOrganizationSession(
if err == nil && samlConfig.EnforcementPolicy == coredata.SAMLEnforcementPolicyRequired { if err == nil && samlConfig.EnforcementPolicy == coredata.SAMLEnforcementPolicyRequired {
if rootSession.AuthMethod != coredata.AuthMethodSAML { if rootSession.AuthMethod != coredata.AuthMethodSAML {
redirectURL, err := s.SAMLService.InitiateLogin(ctx, samlConfig.ID, redirectPath) redirectURL, err := s.SAMLService.InitiateLogin(ctx, samlConfig.ID, continueURL)
if err != nil { if err != nil {
return fmt.Errorf("cannot initiate SAML login: %w", err) return fmt.Errorf("cannot initiate SAML login: %w", err)
} }

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"net/url"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"go.gearno.de/kit/httpserver" "go.gearno.de/kit/httpserver"
@@ -11,6 +12,7 @@ import (
"go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/baseurl"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/iam" "go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/saferedirect"
"go.probo.inc/probo/pkg/securecookie" "go.probo.inc/probo/pkg/securecookie"
"go.probo.inc/probo/pkg/server/api/authn" "go.probo.inc/probo/pkg/server/api/authn"
) )
@@ -20,6 +22,7 @@ type SAMLHandler struct {
sessionCookie *authn.Cookie sessionCookie *authn.Cookie
baseURL *baseurl.BaseURL baseURL *baseurl.BaseURL
logger *log.Logger logger *log.Logger
safeRedirect *saferedirect.SafeRedirect
} }
func NewSAMLHandler(iam *iam.Service, cookieConfig securecookie.Config, baseURL *baseurl.BaseURL, logger *log.Logger) *SAMLHandler { func NewSAMLHandler(iam *iam.Service, cookieConfig securecookie.Config, baseURL *baseurl.BaseURL, logger *log.Logger) *SAMLHandler {
@@ -28,10 +31,11 @@ func NewSAMLHandler(iam *iam.Service, cookieConfig securecookie.Config, baseURL
sessionCookie: authn.NewCookie(&cookieConfig), sessionCookie: authn.NewCookie(&cookieConfig),
baseURL: baseURL, baseURL: baseURL,
logger: logger, logger: logger,
safeRedirect: &saferedirect.SafeRedirect{AllowedHost: baseURL.Host()},
} }
} }
func (h *SAMLHandler) renderInternalServerError(w http.ResponseWriter, r *http.Request) { func (h *SAMLHandler) renderInternalServerError(w http.ResponseWriter) {
httpserver.RenderError(w, http.StatusInternalServerError, errors.New("internal server error")) httpserver.RenderError(w, http.StatusInternalServerError, errors.New("internal server error"))
} }
@@ -63,7 +67,7 @@ func (h *SAMLHandler) ConsumeHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
configIDStr := relayState[:gid.EncodedGIDSize+1] configIDStr := relayState[:gid.EncodedGIDSize]
if configIDStr == "" { if configIDStr == "" {
httpserver.RenderError(w, http.StatusBadRequest, errors.New("missing config ID")) httpserver.RenderError(w, http.StatusBadRequest, errors.New("missing config ID"))
return return
@@ -75,16 +79,21 @@ func (h *SAMLHandler) ConsumeHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
redirectPath := relayState[gid.EncodedGIDSize+1:]
user, membership, err := h.iam.SAMLService.HandleAssertion(ctx, samlResponse, configID) user, membership, err := h.iam.SAMLService.HandleAssertion(ctx, samlResponse, configID)
if err != nil { if err != nil {
httpserver.RenderError(w, http.StatusUnauthorized, err) httpserver.RenderError(w, http.StatusUnauthorized, err)
return return
} }
if redirectPath == "" { continueURL := "/organizations/" + membership.OrganizationID.String()
redirectPath = "/organizations/" + membership.OrganizationID.String() if len(relayState) > gid.EncodedGIDSize {
unescapedContinueURL, err := url.QueryUnescape(relayState[gid.EncodedGIDSize:])
if err != nil {
h.logger.WarnCtx(ctx, "cannot unescape continue URL from RelayState", log.Error(err))
} else {
continueURL = unescapedContinueURL
}
} }
rootSession := authn.SessionFromContext(ctx) rootSession := authn.SessionFromContext(ctx)
@@ -94,21 +103,21 @@ func (h *SAMLHandler) ConsumeHandler(w http.ResponseWriter, r *http.Request) {
rootSession, err = h.iam.AuthService.OpenSessionWithSAML(ctx, user.ID) rootSession, err = h.iam.AuthService.OpenSessionWithSAML(ctx, user.ID)
if err != nil { if err != nil {
h.logger.ErrorCtx(ctx, "cannot open root session", log.Error(err)) h.logger.ErrorCtx(ctx, "cannot open root session", log.Error(err))
h.renderInternalServerError(w, r) h.renderInternalServerError(w)
return return
} }
case rootSession.IdentityID != user.ID: case rootSession.IdentityID != user.ID:
err = h.iam.SessionService.CloseSession(ctx, rootSession.ID) err = h.iam.SessionService.CloseSession(ctx, rootSession.ID)
if err != nil { if err != nil {
h.logger.ErrorCtx(ctx, "cannot close session", log.Error(err)) h.logger.ErrorCtx(ctx, "cannot close session", log.Error(err))
h.renderInternalServerError(w, r) h.renderInternalServerError(w)
return return
} }
rootSession, err = h.iam.AuthService.OpenSessionWithSAML(ctx, user.ID) rootSession, err = h.iam.AuthService.OpenSessionWithSAML(ctx, user.ID)
if err != nil { if err != nil {
h.logger.ErrorCtx(ctx, "cannot open root session", log.Error(err)) h.logger.ErrorCtx(ctx, "cannot open root session", log.Error(err))
h.renderInternalServerError(w, r) h.renderInternalServerError(w)
return return
} }
} }
@@ -116,14 +125,13 @@ func (h *SAMLHandler) ConsumeHandler(w http.ResponseWriter, r *http.Request) {
_, _, err = h.iam.SessionService.OpenSAMLChildSessionForOrganization(ctx, rootSession.ID, membership.OrganizationID) _, _, err = h.iam.SessionService.OpenSAMLChildSessionForOrganization(ctx, rootSession.ID, membership.OrganizationID)
if err != nil { if err != nil {
h.logger.ErrorCtx(ctx, "cannot open SAML child session", log.Error(err)) h.logger.ErrorCtx(ctx, "cannot open SAML child session", log.Error(err))
h.renderInternalServerError(w, r) h.renderInternalServerError(w)
return return
} }
h.sessionCookie.Set(w, rootSession) h.sessionCookie.Set(w, rootSession)
redirectURL := h.baseURL.WithPath(redirectPath).MustString() h.safeRedirect.Redirect(w, r, continueURL, "/organizations/"+membership.OrganizationID.String(), http.StatusFound)
http.Redirect(w, r, redirectURL, http.StatusFound)
} }
func (h *SAMLHandler) LoginHandler(w http.ResponseWriter, r *http.Request) { func (h *SAMLHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
@@ -135,7 +143,7 @@ func (h *SAMLHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
redirectPathQueryParam := r.URL.Query().Get("redirect-path") continueURLQueryParam := r.URL.Query().Get("continue")
samlConfigID, err := gid.ParseGID(samlConfigIDParam) samlConfigID, err := gid.ParseGID(samlConfigIDParam)
if err != nil { if err != nil {
@@ -143,7 +151,7 @@ func (h *SAMLHandler) LoginHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
url, err := h.iam.SAMLService.InitiateLogin(ctx, samlConfigID, redirectPathQueryParam) url, err := h.iam.SAMLService.InitiateLogin(ctx, samlConfigID, continueURLQueryParam)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot initiate SAML login: %w", err)) panic(fmt.Errorf("cannot initiate SAML login: %w", err))
} }

View File

@@ -669,7 +669,7 @@ input ChangeEmailInput {
input AssumeOrganizationSessionInput { input AssumeOrganizationSessionInput {
organizationId: ID! organizationId: ID!
redirectPath: String! continue: String!
} }
input RevokeSessionInput { input RevokeSessionInput {

View File

@@ -246,7 +246,7 @@ type ComplexityRoot struct {
UpdateMembership func(childComplexity int, input types.UpdateMembershipInput) int UpdateMembership func(childComplexity int, input types.UpdateMembershipInput) int
UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int
UpdateSAMLConfiguration func(childComplexity int, input types.UpdateSAMLConfigurationInput) int UpdateSAMLConfiguration func(childComplexity int, input types.UpdateSAMLConfigurationInput) int
UpdateSCIMBridge func(childComplexity int, input types.UpdateSCIMBridgeInput) int UpdateSCIMBridge func(childComplexity int, input types.UpdateSCIMBridgeInput) int
VerifyEmail func(childComplexity int, input types.VerifyEmailInput) int VerifyEmail func(childComplexity int, input types.VerifyEmailInput) int
} }
@@ -380,7 +380,7 @@ type ComplexityRoot struct {
SCIMBridge struct { SCIMBridge struct {
Connector func(childComplexity int) int Connector func(childComplexity int) int
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
ExcludedUserNames func(childComplexity int) int ExcludedUserNames func(childComplexity int) int
ID func(childComplexity int) int ID func(childComplexity int) int
Permission func(childComplexity int, action string) int Permission func(childComplexity int, action string) int
ScimConfiguration func(childComplexity int) int ScimConfiguration func(childComplexity int) int
@@ -3111,7 +3111,7 @@ input ChangeEmailInput {
input AssumeOrganizationSessionInput { input AssumeOrganizationSessionInput {
organizationId: ID! organizationId: ID!
redirectPath: String! continue: String!
} }
input RevokeSessionInput { input RevokeSessionInput {
@@ -14931,7 +14931,7 @@ func (ec *executionContext) unmarshalInputAssumeOrganizationSessionInput(ctx con
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"organizationId", "redirectPath"} fieldsInOrder := [...]string{"organizationId", "continue"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@@ -14945,13 +14945,13 @@ func (ec *executionContext) unmarshalInputAssumeOrganizationSessionInput(ctx con
return it, err return it, err
} }
it.OrganizationID = data it.OrganizationID = data
case "redirectPath": case "continue":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("redirectPath")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("continue"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalNString2string(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
it.RedirectPath = data it.Continue = data
} }
} }

View File

@@ -36,7 +36,7 @@ type AcceptInvitationPayload struct {
type AssumeOrganizationSessionInput struct { type AssumeOrganizationSessionInput struct {
OrganizationID gid.GID `json:"organizationId"` OrganizationID gid.GID `json:"organizationId"`
RedirectPath string `json:"redirectPath"` Continue string `json:"continue"`
} }
type AssumeOrganizationSessionPayload struct { type AssumeOrganizationSessionPayload struct {
@@ -416,7 +416,7 @@ type SCIMBridge struct {
ScimConfiguration *SCIMConfiguration `json:"scimConfiguration,omitempty"` ScimConfiguration *SCIMConfiguration `json:"scimConfiguration,omitempty"`
Connector *Connector `json:"connector,omitempty"` Connector *Connector `json:"connector,omitempty"`
Type coredata.SCIMBridgeType `json:"type"` Type coredata.SCIMBridgeType `json:"type"`
ExcludedUserNames []string `json:"excludedUserNames"` ExcludedUserNames []string `json:"excludedUserNames"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"` UpdatedAt time.Time `json:"updatedAt"`
Permission bool `json:"permission"` Permission bool `json:"permission"`
@@ -561,9 +561,9 @@ type UpdateSAMLConfigurationPayload struct {
} }
type UpdateSCIMBridgeInput struct { type UpdateSCIMBridgeInput struct {
OrganizationID gid.GID `json:"organizationId"` OrganizationID gid.GID `json:"organizationId"`
ScimBridgeID gid.GID `json:"scimBridgeId"` ScimBridgeID gid.GID `json:"scimBridgeId"`
ExcludedUserNames []string `json:"excludedUserNames"` ExcludedUserNames []string `json:"excludedUserNames"`
} }
type UpdateSCIMBridgePayload struct { type UpdateSCIMBridgePayload struct {

View File

@@ -406,6 +406,9 @@ func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput)
} }
} }
w := gqlutils.HTTPResponseWriterFromContext(ctx)
r.sessionCookie.Set(w, session)
if input.OrganizationID != nil { if input.OrganizationID != nil {
var err error var err error
_, _, err = r.iam.SessionService.OpenPasswordChildSessionForOrganization(ctx, session.ID, *input.OrganizationID) _, _, err = r.iam.SessionService.OpenPasswordChildSessionForOrganization(ctx, session.ID, *input.OrganizationID)
@@ -415,7 +418,7 @@ func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput)
var errMembershipInactive *iam.ErrMembershipInactive var errMembershipInactive *iam.ErrMembershipInactive
if errors.As(err, &errMembershipNotFound) || errors.As(err, &errMembershipInactive) { if errors.As(err, &errMembershipNotFound) || errors.As(err, &errMembershipInactive) {
return nil, gqlutils.Forbidden(ctx, err) return nil, gqlutils.Forbiddenf(ctx, "forbidden")
} }
r.logger.ErrorCtx(ctx, "cannot assume organization", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot assume organization", log.Error(err))
@@ -423,9 +426,6 @@ func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput)
} }
} }
w := gqlutils.HTTPResponseWriterFromContext(ctx)
r.sessionCookie.Set(w, session)
return &types.SignInPayload{ return &types.SignInPayload{
Identity: types.NewIdentity(identity), Identity: types.NewIdentity(identity),
Session: types.NewSession(session), Session: types.NewSession(session),
@@ -680,7 +680,7 @@ func (r *mutationResolver) ChangeEmail(ctx context.Context, input types.ChangeEm
func (r *mutationResolver) AssumeOrganizationSession(ctx context.Context, input types.AssumeOrganizationSessionInput) (*types.AssumeOrganizationSessionPayload, error) { func (r *mutationResolver) AssumeOrganizationSession(ctx context.Context, input types.AssumeOrganizationSessionInput) (*types.AssumeOrganizationSessionPayload, error) {
rootSession := authn.SessionFromContext(ctx) rootSession := authn.SessionFromContext(ctx)
childSession, membership, err := r.iam.SessionService.AssumeOrganizationSession(ctx, rootSession.ID, input.OrganizationID, input.RedirectPath) childSession, membership, err := r.iam.SessionService.AssumeOrganizationSession(ctx, rootSession.ID, input.OrganizationID, input.Continue)
if err != nil { if err != nil {
var ( var (
errMembershipNotFound *iam.ErrMembershipNotFound errMembershipNotFound *iam.ErrMembershipNotFound