K
Kindeβ€’12mo ago
EllipticElysium

typescript SDK session manager

hey, i'm going through the typescript sdk docs (i'm adding to a bunJs + ElysiaJs app) - https://kinde.com/docs/developer-tools/typescript-sdk/ and I'm at the section about sessionManager and it says This would need to expanded to handle multiple sessions. is this something that we're expected to build ourselves, or is this a common interface where i can just pass redis into here or smthn?
Kinde Docs
TypeScript SDK - Developer tools - Help center
Our developer tools provide everything you need to get started with Kinde.
30 Replies
EllipticElysium
EllipticElysiumOPβ€’12mo ago
after looking into further, i'm a little confused about the sessionManager in general. as it sets all the tokens etc... directly against the root, and not nested under a uuid or anything, so sessionManager doesn't seem to be a manager at all, looks to be an individual session. i could handle that, and build a manager myself, but kinde doesn't return a uuid anywhere earlier in the process for to find and pass the correct session to the flow. is the first time i've had to work directly with authentication and sessions etc... seem to have always joined projects after it's all set up the past few years. so is possible it's blindingly obvious and i'm just missing something here hmm. yeah, everything seems to just use sessionManger as a parameter, and assumes you have the correct session already. but has no details on when a new api request comes in, how you relate the cookie sent in the header to a specific session your storing sorry. that's just a brain dump of me trying to figure out what's going on πŸ˜… but if anyone could provide some explanation or examples of how it all fits together, i'd be v grateful :)
Oli - Kinde
Oli - Kindeβ€’12mo ago
Hey @EllipticElysium, Thanks for reaching out. I'll speak to my TypeScript expert teammate tomorrow and get back to you
EllipticElysium
EllipticElysiumOPβ€’12mo ago
hey @Oli - Kinde just wanted to follow up on this, did you manage to get any update? i know is christmas ofc, so no worries at all if not yet. but would you be able to provide an estimated time? :) πŸŽ„
Oli - Kinde
Oli - Kindeβ€’12mo ago
Hey @EllipticElysium, We are still looking into your issue. As you mentioned it is the holiday period (Christmas period), so we have limited staff online. Whilst I cannot give you a concrete estimate as to when this TypeScipt issue you are experiencing will be resolved, I can say it will be likely early next year (early Jan 2024). I will get back to you whenever I have updates, apologies for the inconvenience.
EllipticElysium
EllipticElysiumOPβ€’12mo ago
No worries, I'll check back in the new year. Merry Christmas :)
Oli - Kinde
Oli - Kindeβ€’12mo ago
Merry Christmas to you too! And happy new year!
Bagus
Bagusβ€’11mo ago
Hi @EllipticElysium , what framework you're using to connect with Kinde? Server side or browser? I had this kind of confusion as well when integrating it with solid-start. No official SDK, so I use Typescript SDK (as server side usage). Also I found it less-guide on proper sessionManager πŸ˜„. But finally managed it to work properly using session cookie.
EllipticElysium
EllipticElysiumOPβ€’11mo ago
I'm on the server side using BunJs and ElysiaJs. There won't be any direct sdks, so just assume it's pure typescript without express (though the docs seems to assume it's use) You managed to figure out how to get it working with the typescript?
Bagus
Bagusβ€’11mo ago
Yes
EllipticElysium
EllipticElysiumOPβ€’11mo ago
Can you share any details of what worked that might be relevant to my issue? Hey @Oli - Kinde hope you had a good Christmas/New-year. Have you had a chance to look into this any further? :)
Oli - Kinde
Oli - Kindeβ€’11mo ago
Happy new year @EllipticElysium, Now that most of my team is back online, I will reach out to them and get an update on this issue for you.
Oli - Kinde
Oli - Kindeβ€’11mo ago
Hi @EllipticElysium, Sorry for the delayed response. So you will need to warp a session manager/store of your choice. An example of a session manager is: https://github.com/gaurishhs/elysia-session FYI, we are replacing this section in the Typescript SDK doc to say the following:
This would work for a single user for local development purposes, but would need to be expanded for a production environment. The appropriate session store for your application will depend on your application architecture, for example encrypted cookies in a stateless server environment or a shared cache/database for a load balanced cluster of servers. Commonly, the session manager will be a wrapper around an existing session management library - often provided by a web framework, or a third party library.
Please let me know if you have any other questions.
GitHub
GitHub - gaurishhs/elysia-session: Sessions plugin for Elysia
Sessions plugin for Elysia. Contribute to gaurishhs/elysia-session development by creating an account on GitHub.
EllipticElysium
EllipticElysiumOPβ€’11mo ago
Thanks I'll take a look through that and see if I can get it working. Likely to be the weekend though :)
Oli - Kinde
Oli - Kindeβ€’11mo ago
No worries. Please reach out if you come across more questions or issues.
Zanoryt
Zanorytβ€’11mo ago
I am looking for some help here as well. I have a Vue app with TypeScript, and I want to figure out the best way to implement the session manager to coexist with my store (as well as browser storage and/or cookies).
Oli - Kinde
Oli - Kindeβ€’11mo ago
Hey @Zanoryt, What SDK are you using?
Zanoryt
Zanorytβ€’11mo ago
typescript sdk
Oli - Kinde
Oli - Kindeβ€’11mo ago
Okay I will reach out to my team and get back to you @Zanoryt
leo_kinde
leo_kindeβ€’11mo ago
Hi @Zanoryt , assuming your app is client-side, our recommendation is to not store the tokens in session/local storage, or cookies accessible from JS due to potential for the tokens to be stolen by cross-site scripts. We create a HTTP only cookie that can be used to maintain the user's logged in session if you configure a custom domain in Kinde. For local development you can override the session manager to use local or session storage. You can also use our original browser specific SDK: https://www.npmjs.com/package/@kinde-oss/kinde-auth-pkce-js which has this functionality built in with an option to toggle to use local storage: https://kinde.com/docs/developer-tools/javascript-sdk/#token-storage-in-the-authentication-state Just for reference the TypeScript SDK is a newer SDK which we've started using as a base for the other JS SDKs, but not all of them are using it yet.
npm
@kinde-oss/kinde-auth-pkce-js
Kinde PKCE authentication for SPAs. Latest version: 3.0.27, last published: 2 months ago. Start using @kinde-oss/kinde-auth-pkce-js in your project by running npm i @kinde-oss/kinde-auth-pkce-js. There are 2 other projects in the npm registry using @kinde-oss/kinde-auth-pkce-js.
Kinde Docs
JavaScript SDK - Developer tools - Help center
Our developer tools provide everything you need to get started with Kinde.
Zanoryt
Zanorytβ€’11mo ago
Hey @leo_kinde thank you so much for this response! What is your recommendation? Pass the token off to the backend and create a session cookie with the backend?
leo_kinde
leo_kindeβ€’11mo ago
@Zanoryt , for frontend apps our recommendation is to use a custom domain and the HTTP only cookie as this is the simplest setup. Then pass the access token to any backend services that require authenticated access (backend services can independently validate the token).
EllipticElysium
EllipticElysiumOPβ€’11mo ago
hi Oli, I've only just had a chance to take another look at this, but i'm still confused about the uuid issue i mentioned. There is nothing i can see in the docs about how to uniquely identify an individual session in order to pass that to kindeClient.login(...) or any other function. building a wrapper is easy if i have a uuid to set and retrieve sessions, but without one 🀷 Looking into just assigning a uuid myself, easy enough to start. i can create a session with that uuid, and pass that session to kindeClient.login(session), that's all good. but when i retrieve the redirect after a successful login, i need to pass the same session into kindeClient.handleRedirectToApp(session, url) or it will fail. so somehow, i need to find the session that the request was initiated with. regardless of which sessionManagement i use, i still need to retrieve a specific session to pass into all the Kinde functions. and i've not seen anything in the docs talking about this so, it looks like ac-state-key is what persists between the two requests. I can work with that, but it feels super hacky to intercept the setting of a specific key in order to use that as a key to store my session against. surely there's a better way of doing this? from what I can see, it would be infinitely simpler if the login flow were something like this:
.get('/login', async ({ set }) => {
const session = sessionManager.createSession();
const { sessionId, loginUrl } = await kindeClient.login(session);
sessionManager.saveSession(sessionId, session);
set.redirect = loginUrl.toString();
})
.get('/login-success', async ({ set, path, query }) => {
const session = sessionManager[query.sessionId];
const url = new URL(`http://localhost:3000${path}?code=${query.code}&scope=${query.scope}&state=${query.state}`);
await kindeClient.handleRedirectToApp(session, url);
set.redirect = "/";
})
.get('/login', async ({ set }) => {
const session = sessionManager.createSession();
const { sessionId, loginUrl } = await kindeClient.login(session);
sessionManager.saveSession(sessionId, session);
set.redirect = loginUrl.toString();
})
.get('/login-success', async ({ set, path, query }) => {
const session = sessionManager[query.sessionId];
const url = new URL(`http://localhost:3000${path}?code=${query.code}&scope=${query.scope}&state=${query.state}`);
await kindeClient.handleRedirectToApp(session, url);
set.redirect = "/";
})
and that seems like a fairly simple thing to do. So i'm assuming there's something i'm missing or just not understanding here...
EllipticElysium
EllipticElysiumOPβ€’11mo ago
this might be easier to see rather than the discord code snippet
No description
leo_kinde
leo_kindeβ€’11mo ago
Hi @EllipticElysium , The ac-state-key you mention is the state as outlined in the OAuth2 spec and one of the uses is to verify that the callback is related to an auth flow initiated by the client/user for securiy purposes. So I believe using state to look up the user session would be counter to this and potentially introduce security risk. A session id, if used, would typically be stored in a cookie. I believe this is common in many web frameworks and is often handled so it is not required when interacting with the store. So the SDK is expecting a session store already scoped to the user session. Thank you for trying out Kinde with a new framework. ElysiaJS looks quite interesting. If you come up with code that makes integrating with Kinde easier, we'd love for you to share, having an ElysiaJS specifc SDK would make it easier for others to use. Any further questions, just let us know.
EllipticElysium
EllipticElysiumOPβ€’11mo ago
awesome, thanks. I'll give that a try :) though i would still query the naming of sessionManager instead of session but perhaps that's just my personal style/preference πŸ˜›
leo_kinde
leo_kindeβ€’11mo ago
Thanks for the feedback @EllipticElysium , I can see how the manager naming could be misleading, definitely something for us to consider.
EllipticElysium
EllipticElysiumOPβ€’11mo ago
have done a basic PoC and have got it working, thanks for your help. the key bit i was missing was just not knowing that storing sessionId in cookies was "common practice" here's a rough example for a PoC. I'll write it properly and share that when i get the chance
import { Elysia } from "elysia";
import { cors } from '@elysiajs/cors'
import { cookie } from '@elysiajs/cookie'
import { createKindeServerClient, GrantType } from "@kinde-oss/kinde-typescript-sdk";
import config from "./config";

const kindeClient = createKindeServerClient(GrantType.AUTHORIZATION_CODE, {
authDomain: config.kinde.domain,
clientId: config.kinde.clientId,
clientSecret: config.kinde.clientSecret,
redirectURL: config.kinde.redirectUri,
logoutRedirectURL: config.kinde.logoutUri,
});

class Session {
private store: Record<string, unknown> = {};

constructor(public sessionId: string) {
this.sessionId = sessionId;
}

public async getSessionItem(key: string): Promise<unknown> {
return this.store[key];
}

public async setSessionItem(key: string, value: unknown): Promise<void> {
this.store[key] = value;
}

public async removeSessionItem(key: string): Promise<void> {
delete this.store[key];
}

public async destroySession(): Promise<void> {
this.store = {};
SessionManager.destroySession(this.sessionId);
}
}

class SessionManager {
private static sessions: Record<string, Session> = {};

public static createSession(): Session {
const sessionId = crypto.randomUUID();
const session = new Session(sessionId);
this.sessions[sessionId] = session;
return session;
}

public static getSession(sessionId: string): Session | undefined {
return this.sessions[sessionId];
}

public static destroySession(sessionId: string): void {
delete this.sessions[sessionId];
}
}
import { Elysia } from "elysia";
import { cors } from '@elysiajs/cors'
import { cookie } from '@elysiajs/cookie'
import { createKindeServerClient, GrantType } from "@kinde-oss/kinde-typescript-sdk";
import config from "./config";

const kindeClient = createKindeServerClient(GrantType.AUTHORIZATION_CODE, {
authDomain: config.kinde.domain,
clientId: config.kinde.clientId,
clientSecret: config.kinde.clientSecret,
redirectURL: config.kinde.redirectUri,
logoutRedirectURL: config.kinde.logoutUri,
});

class Session {
private store: Record<string, unknown> = {};

constructor(public sessionId: string) {
this.sessionId = sessionId;
}

public async getSessionItem(key: string): Promise<unknown> {
return this.store[key];
}

public async setSessionItem(key: string, value: unknown): Promise<void> {
this.store[key] = value;
}

public async removeSessionItem(key: string): Promise<void> {
delete this.store[key];
}

public async destroySession(): Promise<void> {
this.store = {};
SessionManager.destroySession(this.sessionId);
}
}

class SessionManager {
private static sessions: Record<string, Session> = {};

public static createSession(): Session {
const sessionId = crypto.randomUUID();
const session = new Session(sessionId);
this.sessions[sessionId] = session;
return session;
}

public static getSession(sessionId: string): Session | undefined {
return this.sessions[sessionId];
}

public static destroySession(sessionId: string): void {
delete this.sessions[sessionId];
}
}
const app = new Elysia()
.use(cors())
.use(cookie())
.get('/login', async ({ set, setCookie }) => {
const session = SessionManager.createSession();
const loginUrl = await kindeClient.login(session);
setCookie('sessionId', session.sessionId, {
httpOnly: true
})

set.redirect = loginUrl.toString();
})
.get('/login-success', async ({ cookie: { sessionId }, set, path, query }) => {
const session = SessionManager.getSession(sessionId);

if (session) {
const url = new URL(`http://localhost:3000${path}?code=${query.code}&scope=${query.scope}&state=${query.state}`);
await kindeClient.handleRedirectToApp(session, url);

set.redirect = "/";
} else {
throw new Error("Session not found");
}
})
.get("/", () => {
// return SessionManager.sessions;
})
.listen(config.port);
const app = new Elysia()
.use(cors())
.use(cookie())
.get('/login', async ({ set, setCookie }) => {
const session = SessionManager.createSession();
const loginUrl = await kindeClient.login(session);
setCookie('sessionId', session.sessionId, {
httpOnly: true
})

set.redirect = loginUrl.toString();
})
.get('/login-success', async ({ cookie: { sessionId }, set, path, query }) => {
const session = SessionManager.getSession(sessionId);

if (session) {
const url = new URL(`http://localhost:3000${path}?code=${query.code}&scope=${query.scope}&state=${query.state}`);
await kindeClient.handleRedirectToApp(session, url);

set.redirect = "/";
} else {
throw new Error("Session not found");
}
})
.get("/", () => {
// return SessionManager.sessions;
})
.listen(config.port);
EllipticElysium
EllipticElysiumOPβ€’10mo ago
GitHub
GitHub - ElysiumVentura/bun-elysia-kinde
Contribute to ElysiumVentura/bun-elysia-kinde development by creating an account on GitHub.
leo_kinde
leo_kindeβ€’10mo ago
That's awesome, thanks for sharing @EllipticElysium . I see this version uses Redis, so should be more robust than using memory πŸ‘
EllipticElysium
EllipticElysiumOPβ€’10mo ago
Yeah, in memory was just a poc so I could figure it out before complicating anything. Thanks for the help getting it sorted :)
Want results from more Discord servers?
Add your server