Regardless of whether one CAN share cookies cross origin (which IIUC is actually possible), that is not the usual (or, likely, good) way to solve this problem.
Here are the basics of how I ultimately achieved SSO for my svelte-kit app using my Wix hosted site acting as the identity provider in a manner that is completely transparent and touch-free for the end user.
(Note: I spent WAY more time on this than it would have taken me to wire up industrial strength SSO using a provider like auth0. But, it was educational and I hope this efforts will shed some light on SSO mechanics for other interested parties. Needless to say I make no warranties as to the functionality or security of this code. Obviously, something like auth0 would be much more appropriate if you are trying to protect anything of value.)
The over all flow is:
Svelte-kit App redirects to Wix site passing desired session details as query term, then...
Wix site verifies that there is a user logged in, stores desired session details, redirects back to App with session token as query term, then...
App POSTs token back to get identity details and then sets session cookie, and redirects to originally requested protected URI.
Implementation
First, within my svelte-kit app, we redirect to the identity provider for protected routes if there is not a valid session cookie, keeping track of the requested URL and the authorization endpoint via query parameters. (Note that the HTTP response code is important--if you use 301, the permanent redirect will be cached by many browsers and then you will "never" be able to navigate back to your protected routes.)
host.config.js
const BASEURL = "https://MyAmazingWebApp.com/";
export const handle = async ({ event, resolve }) => {const session =
event.cookies.get('session');
const path = event.url.pathname;
if (!session) {
const regex = /^\/public/g;
const pub = path.match(regex);
if(!pub) {
throw redirect(307, process.env.HIGHFLASH_SSO_URI + "?" +
"requested_url=" + event.url +
"&auth_url=" + BASEURL + "public/auth/sso");
} else {
// public route ... proceed with request
return await resolve(event)
}
}
Now on my Wix hosted site, I create a GET
route to respond to authentication requests within a backend script.
http-functions.js
// Need this to actually get emails. Seems odd, but...
const collection_opts = {
"suppressAuth": true // !?
}
export async function get_hfpSSO(request) {
var type, id, session_id;
const requested_url = request.query.requested_url;
const auth_url = request.query.auth_url;
// retrieve id of logged in user, if any
try {
type = ident.identificationData.identities[0].person.type;
id = ident.identificationData.identities[0].person.id;
} catch(e) {
type = "VISITOR"
}
if(type != "VISITOR") {
/*
store the requested session details and the user's id as a
signed JWT in a DB (collection) on wix hosted site. Set
'session_id' to UUID of this row in DB so we can retrieve
these details after successful handshake
/*
} else {
session_id = "";
}
let res_options = {
status: 307,
headers: {
"Location" : auth_url + "?session_token=" + session_id
}
};
return response(res_options);
}
Back in my svelte-kit app, I define an endpoint to receive the authentication token and complete the handshake via POST. (I don't know if this is ultimately more secure then just using query terms, and I haven't studied the browser network traffic to verify). Note: After the handhsake is complete, I use an HTML meta refresh tag to go to the originally requested page instead of a redirect because I need to set a cookie. I cannot set a cookie with a redirect response.
The data returned by the POST includes a signed JWT that I verify with the corresponding public key stored locally.
$src/routes/public/sso/+server.js
import jwt from 'jsonwebtoken';
import fs from 'fs';
import { nanoid } from 'nanoid';
const BASEURL = "https://MyAmaxingWebApp.com/";
export async function GET({ request, url }) {
var session_token, destination;
const idp_session_token = url.searchParams.get('session_token');
if(idp_session_token) {
const response = await fetch("https://www.highflowpeds.com/_functions/hfpSSO",
{
method: 'POST',
headers: { "Content-Type": "application/json" },
body: JSON.stringify({"session_token": idp_session_token})
})
const body = await response.json();
const session = {token: body.auth_token, destination: body.destination};
try {
// verify JWT signature of session.token, then...
session_token = nanoid();
destination = session.destination;
} catch(err) {
session_token = "";
destination = BASEURL + "public/auth/login";
}
} else {
session_token = "";
destination = BASEURL + "public/auth/login";
}
let res = new Response("<html><head><meta http-equiv='Refresh' content='0; url=" + destination + "'><head></html>");
res.headers.append("Content-Type", "text/html; charset=utf-8");
res.headers.append("Set-Cookie", "session=" + session_token + "; SameSite=Lax; HttpOnly; Path=/; Max-Age=86400");
return res;
}
On the Wix site end, this is the POST endpoint that provides the identity details as a signed JWT in response to a valid session token:
export async function post_hfpSSO(request) {
let response;
const d = await request.body.json();
const session_token = d.session_token;
if (session_token) {
// loading up the session details I saved to DB during the initial GET request.
const session = await wixData.query("SSO_sessions")
.eq("session_id", session_token)
.find(collection_opts);
response = {
body: {auth_token: session.items[0].auth_token, destination: session.items[0].destination},
headers: {
"Content-Type": "application/json"
}
};
} else {
response = {
body: {auth_token: "", destination: ""},
headers: {
"Content-Type": "application/json"
}
}
};
return ok(response);
}