Implementing logout (Scalekit FSA)
Goal
Implement a single /logout endpoint that:
- Clears the application session layer (your cookies/tokens).
- Invalidates the Scalekit session layer by redirecting the browser to Scalekit’s OIDC logout endpoint.
- Returns the user to a safe, allowlisted post-logout redirect URL.
Key constraints (must follow)
- The Scalekit logout call MUST be a browser redirect (top-level navigation), not a
fetch/XHR from frontend and not a server-to-server API call.
- The ID token (often
idToken) MUST be read BEFORE clearing cookies, because it is used as id_token_hint.
- The
post_logout_redirect_uri MUST be allowlisted in Scalekit Dashboard (Post Logout URLs).
Inputs to collect from the user/project
Ask for (or infer from the codebase):
- Tech stack: Express/Fastify/Next.js (Node), Flask/Django (Python), Gin/Fiber (Go), Spring Boot (Java), etc.
- Where tokens are stored: cookie names (default examples:
accessToken, refreshToken, idToken) and cookie attributes (Path, Domain, SameSite).
- The post-logout landing URL (example:
http://localhost:3000/login or your production login page).
- Scalekit configuration: base URL / environment, and whether the project uses a Scalekit SDK helper like
getLogoutUrl(...).
Recommended implementation (workflow)
- Locate the current auth/session code:
- Find where access/refresh/ID tokens are set.
- Note cookie names, paths, domains, and SameSite settings (you must match these when clearing).
- Add a GET
/logout route:
- Extract
idToken (or equivalent) from cookies/session storage.
- Compute
postLogoutRedirectUri.
- Build the Scalekit logout URL pointing at
/oidc/logout, preferably using the Scalekit SDK helper if present.
- Clear session cookies (access/refresh/id), preserving the correct Path/Domain so deletion actually works.
- Redirect (302) the browser to the Scalekit logout URL.
- Configure Scalekit Dashboard allowlist:
- Register
postLogoutRedirectUri under: Redirects → Post Logout URL.
- Verify and iterate:
- In DevTools → Network, clicking logout should show a document navigation to Scalekit (not XHR/fetch).
- Confirm the request includes the Scalekit session cookie automatically.
- After redirecting back, logging in should not silently reuse the application cookies you intended to clear.
Reference behavior (pseudocode)
- Read
id_token_hint from cookie/session.
logoutUrl = scalekit.getLogoutUrl(id_token_hint, post_logout_redirect_uri)
- Clear cookies (access/refresh/id).
302 -> logoutUrl
Implementation templates
Node.js (Express)
app.get('/logout', (req, res) => {
const idTokenHint = req.cookies?.idToken; // read BEFORE clearing
const postLogoutRedirectUri = process.env.POST_LOGOUT_REDIRECT_URI ?? 'http://localhost:3000/login';
// Prefer SDK helper if available in your project
const logoutUrl = scalekit.getLogoutUrl(idTokenHint, postLogoutRedirectUri);
// Clear cookies (match Path/Domain/SameSite used when setting them)
res.clearCookie('accessToken', { path: '/' });
res.clearCookie('refreshToken', { path: '/' });
res.clearCookie('idToken', { path: '/' });
return res.redirect(logoutUrl);
});
Python (Flask)
from flask import request, redirect, make_response
@app.get("/logout")
def logout():
id_token = request.cookies.get("idToken") # read BEFORE clearing
post_logout_redirect_uri = os.getenv("POST_LOGOUT_REDIRECT_URI", "http://localhost:3000/login")
logout_url = scalekit_client.get_logout_url(
id_token_hint=id_token,
post_logout_redirect_uri=post_logout_redirect_uri
)
resp = make_response(redirect(logout_url))
resp.set_cookie("accessToken", "", max_age=0, path="/")
resp.set_cookie("refreshToken", "", max_age=0, path="/")
resp.set_cookie("idToken", "", max_age=0, path="/")
return resp
Go (Gin)
func LogoutHandler(c *gin.Context) {
idToken, _ := c.Cookie("idToken") // read BEFORE clearing
postLogoutRedirectURI := os.Getenv("POST_LOGOUT_REDIRECT_URI")
if postLogoutRedirectURI == "" {
postLogoutRedirectURI = "http://localhost:3000/login"
}
logoutURL, err := scalekit.GetLogoutUrl(scalekit.LogoutUrlOptions{
IdTokenHint: idToken,
PostLogoutRedirectUri: postLogoutRedirectURI,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Clear cookies (match original attributes)
c.SetCookie("accessToken", "", -1, "/", "", true, true)
c.SetCookie("refreshToken", "", -1, "/", "", true, true)
c.SetCookie("idToken", "", -1, "/", "", true, true)
c.Redirect(http.StatusFound, logoutURL.String())
}
Java (Spring Boot)
@GetMapping("/logout")
public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {
String idToken = null;
if (request.getCookies() != null) {
for (Cookie c : request.getCookies()) {
if ("idToken".equals(c.getName())) {
idToken = c.getValue();
break;
}
}
}
String postLogoutRedirectUri = System.getenv().getOrDefault(
"POST_LOGOUT_REDIRECT_URI",
"http://localhost:3000/login"
);
URL logoutUrl = scalekitClient.authentication().getLogoutUrl(
idToken,
postLogoutRedirectUri
);
// Clear cookies (ensure Path/Domain match your app cookies)
Cookie access = new Cookie("accessToken", "");
access.setMaxAge(0);
access.setPath("/");
access.setHttpOnly(true);
access.setSecure(true);
response.addCookie(access);
Cookie refresh = new Cookie("refreshToken", "");
refresh.setMaxAge(0);
refresh.setPath("/");
refresh.setHttpOnly(true);
refresh.setSecure(true);
response.addCookie(refresh);
Cookie id = new Cookie("idToken", "");
id.setMaxAge(0);
id.setPath("/");
id.setHttpOnly(true);
id.setSecure(true);
response.addCookie(id);
response.sendRedirect(logoutUrl.toString());
}
Logout security checklist (copy/paste)
- Extract ID token BEFORE clearing cookies.
- Clear all application session cookies (access/refresh/id).
- Redirect browser (302) to Scalekit
/oidc/logout via the generated logout URL.
- Ensure
post_logout_redirect_uri is allowlisted in Scalekit dashboard.
- Validate logout is a document navigation (not XHR/fetch) and cookies are actually removed.
Common failure modes (what to check)
- “Logout doesn’t really log out”: cookie deletion mismatches Path/Domain/SameSite; clear cookies with the same attributes used when setting them.
- “Login immediately succeeds after logout”: identity provider session may still be active; this is expected for SSO providers, but your app cookies should still be cleared.
- “Scalekit logout doesn’t take effect”: logout was done via API call rather than browser redirect; use a redirect so the Scalekit session cookie is included automatically.
- “Redirect rejected”:
post_logout_redirect_uri is not allowlisted in Scalekit dashboard.
Output expectations when using this skill
When asked to implement logout in a real repo, the assistant should:
- Identify the correct cookie names and where they are set.
- Implement
/logout with the correct sequence (read id token → build logout URL → clear cookies → redirect).
- Provide a brief test plan and the exact dashboard value to allowlist for post-logout redirect.
1---2name: implementing-fsa-logout3description: Implements a complete logout flow for Scalekit FSA integrations by clearing application session cookies and redirecting the browser to Scalekit’s /oidc/logout endpoint to invalidate the Scalekit session. Use when adding or fixing logout in Node.js, Python, Go, or Java web apps that use Scalekit OIDC.4---56# Implementing logout (Scalekit FSA)78## Goal9Implement a single `/logout` endpoint that:10- Clears the application session layer (your cookies/tokens).11- Invalidates the Scalekit session layer by redirecting the browser to Scalekit’s OIDC logout endpoint.12- Returns the user to a safe, allowlisted post-logout redirect URL.1314## Key constraints (must follow)15- The Scalekit logout call MUST be a browser redirect (top-level navigation), not a `fetch`/XHR from frontend and not a server-to-server API call.16- The ID token (often `idToken`) MUST be read BEFORE clearing cookies, because it is used as `id_token_hint`.17- The `post_logout_redirect_uri` MUST be allowlisted in Scalekit Dashboard (Post Logout URLs).1819## Inputs to collect from the user/project20Ask for (or infer from the codebase):21- Tech stack: Express/Fastify/Next.js (Node), Flask/Django (Python), Gin/Fiber (Go), Spring Boot (Java), etc.22- Where tokens are stored: cookie names (default examples: `accessToken`, `refreshToken`, `idToken`) and cookie attributes (Path, Domain, SameSite).23- The post-logout landing URL (example: `http://localhost:3000/login` or your production login page).24- Scalekit configuration: base URL / environment, and whether the project uses a Scalekit SDK helper like `getLogoutUrl(...)`.2526## Recommended implementation (workflow)271. Locate the current auth/session code:28- Find where access/refresh/ID tokens are set.29- Note cookie names, paths, domains, and SameSite settings (you must match these when clearing).30312. Add a GET `/logout` route:32- Extract `idToken` (or equivalent) from cookies/session storage.33- Compute `postLogoutRedirectUri`.34- Build the Scalekit logout URL pointing at `/oidc/logout`, preferably using the Scalekit SDK helper if present.35- Clear session cookies (access/refresh/id), preserving the correct Path/Domain so deletion actually works.36- Redirect (302) the browser to the Scalekit logout URL.37383. Configure Scalekit Dashboard allowlist:39- Register `postLogoutRedirectUri` under: Redirects → Post Logout URL.40414. Verify and iterate:42- In DevTools → Network, clicking logout should show a **document** navigation to Scalekit (not XHR/fetch).43- Confirm the request includes the Scalekit session cookie automatically.44- After redirecting back, logging in should not silently reuse the application cookies you intended to clear.4546## Reference behavior (pseudocode)47- Read `id_token_hint` from cookie/session.48- `logoutUrl = scalekit.getLogoutUrl(id_token_hint, post_logout_redirect_uri)`49- Clear cookies (access/refresh/id).50- `302 -> logoutUrl`5152## Implementation templates5354### Node.js (Express)55```js56app.get('/logout', (req, res) => {57 const idTokenHint = req.cookies?.idToken; // read BEFORE clearing58 const postLogoutRedirectUri = process.env.POST_LOGOUT_REDIRECT_URI ?? 'http://localhost:3000/login';5960 // Prefer SDK helper if available in your project61 const logoutUrl = scalekit.getLogoutUrl(idTokenHint, postLogoutRedirectUri);6263 // Clear cookies (match Path/Domain/SameSite used when setting them)64 res.clearCookie('accessToken', { path: '/' });65 res.clearCookie('refreshToken', { path: '/' });66 res.clearCookie('idToken', { path: '/' });6768 return res.redirect(logoutUrl);69});70```7172### Python (Flask)73```py74from flask import request, redirect, make_response7576@app.get("/logout")77def logout():78 id_token = request.cookies.get("idToken") # read BEFORE clearing79 post_logout_redirect_uri = os.getenv("POST_LOGOUT_REDIRECT_URI", "http://localhost:3000/login")8081 logout_url = scalekit_client.get_logout_url(82 id_token_hint=id_token,83 post_logout_redirect_uri=post_logout_redirect_uri84 )8586 resp = make_response(redirect(logout_url))87 resp.set_cookie("accessToken", "", max_age=0, path="/")88 resp.set_cookie("refreshToken", "", max_age=0, path="/")89 resp.set_cookie("idToken", "", max_age=0, path="/")90 return resp91```9293### Go (Gin)94```go95func LogoutHandler(c *gin.Context) {96 idToken, _ := c.Cookie("idToken") // read BEFORE clearing97 postLogoutRedirectURI := os.Getenv("POST_LOGOUT_REDIRECT_URI")98 if postLogoutRedirectURI == "" {99 postLogoutRedirectURI = "http://localhost:3000/login"100 }101102 logoutURL, err := scalekit.GetLogoutUrl(scalekit.LogoutUrlOptions{103 IdTokenHint: idToken,104 PostLogoutRedirectUri: postLogoutRedirectURI,105 })106 if err != nil {107 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})108 return109 }110111 // Clear cookies (match original attributes)112 c.SetCookie("accessToken", "", -1, "/", "", true, true)113 c.SetCookie("refreshToken", "", -1, "/", "", true, true)114 c.SetCookie("idToken", "", -1, "/", "", true, true)115116 c.Redirect(http.StatusFound, logoutURL.String())117}118```119120### Java (Spring Boot)121```java122@GetMapping("/logout")123public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {124 String idToken = null;125 if (request.getCookies() != null) {126 for (Cookie c : request.getCookies()) {127 if ("idToken".equals(c.getName())) {128 idToken = c.getValue();129 break;130 }131 }132 }133134 String postLogoutRedirectUri = System.getenv().getOrDefault(135 "POST_LOGOUT_REDIRECT_URI",136 "http://localhost:3000/login"137 );138139 URL logoutUrl = scalekitClient.authentication().getLogoutUrl(140 idToken,141 postLogoutRedirectUri142 );143144 // Clear cookies (ensure Path/Domain match your app cookies)145 Cookie access = new Cookie("accessToken", "");146 access.setMaxAge(0);147 access.setPath("/");148 access.setHttpOnly(true);149 access.setSecure(true);150 response.addCookie(access);151152 Cookie refresh = new Cookie("refreshToken", "");153 refresh.setMaxAge(0);154 refresh.setPath("/");155 refresh.setHttpOnly(true);156 refresh.setSecure(true);157 response.addCookie(refresh);158159 Cookie id = new Cookie("idToken", "");160 id.setMaxAge(0);161 id.setPath("/");162 id.setHttpOnly(true);163 id.setSecure(true);164 response.addCookie(id);165166 response.sendRedirect(logoutUrl.toString());167}168```169170## Logout security checklist (copy/paste)171- Extract ID token BEFORE clearing cookies.172- Clear all application session cookies (access/refresh/id).173- Redirect browser (302) to Scalekit `/oidc/logout` via the generated logout URL.174- Ensure `post_logout_redirect_uri` is allowlisted in Scalekit dashboard.175- Validate logout is a document navigation (not XHR/fetch) and cookies are actually removed.176177## Common failure modes (what to check)178- “Logout doesn’t really log out”: cookie deletion mismatches Path/Domain/SameSite; clear cookies with the same attributes used when setting them.179- “Login immediately succeeds after logout”: identity provider session may still be active; this is expected for SSO providers, but your app cookies should still be cleared.180- “Scalekit logout doesn’t take effect”: logout was done via API call rather than browser redirect; use a redirect so the Scalekit session cookie is included automatically.181- “Redirect rejected”: `post_logout_redirect_uri` is not allowlisted in Scalekit dashboard.182183## Output expectations when using this skill184When asked to implement logout in a real repo, the assistant should:185- Identify the correct cookie names and where they are set.186- Implement `/logout` with the correct sequence (read id token → build logout URL → clear cookies → redirect).187- Provide a brief test plan and the exact dashboard value to allowlist for post-logout redirect.