Hello everyone,
I know that it's not a issue topic, but it would be very helpful for all to see how to correctly implement oidc-client with SSR (.net/nextjs) ?
BR
I don't have any help for you. Maybe check on stack overflow?
I've been working on this recently and have a basic "working" example which isn't ready to be shared yet but I've found the main sticking point to be the use of local/session storage as this state isn't accessible on the server to obtain the access token, which you'll probably want to pass to your API or similar from both the client or the server.
If this is the only functionality you want on the server you could swap out the userStore implementation to one that uses a cookie, I've not researched the security implications of this yet so be warned.
So using next.js as an example you'll create a userStore that can optionally accept NextContext, this includes the req and therefore the cookie, with the logic to use the request rather than document.cookie when on the server. I've included some example code at the bottom of this comment.
If you'd also like to use the callback methods on the server then you'll hit a couple of other issues with both the redirect navigator, which can be replaced with a universal version via settings, unlike the JsonService which uses XMLHttpRequest that won't work on the server.
@brockallen would you be interested in a PR to replace XMLHttpRequest with fetch which I believe is easier to polyfill on the server?
There are a few moving parts so to summarise to make the methods that aren't dependent on an iframe work you'll need to;
I'm sure there are other considerations which I've not found yet too.
Cooke State Store Example
import { StateStore } from "oidc-client";
import { parseCookies, setCookie, destroyCookie } from "nookies";
import { NextContext } from "next";
interface ICookieStateStore {
prefix?: string;
ctx?: NextContext;
}
export default class CookieStateStore implements StateStore {
private prefix: string;
private ctx?: NextContext;
constructor({ prefix = "oidc.", ctx }: ICookieStateStore) {
this.ctx = ctx;
this.prefix = prefix;
}
public set(key: string, value: any): Promise<void> {
setCookie(this.ctx, this.getKey(key), value, {
path: "/"
});
return Promise.resolve();
}
public get(key: string): Promise<any> {
const value = parseCookies(this.ctx)[this.getKey(key)];
if (value) {
return Promise.resolve(value);
} else {
return Promise.resolve(value);
}
}
public remove(key: string): Promise<any> {
const value = parseCookies(this.ctx)[this.getKey(key)];
destroyCookie(this.ctx, this.getKey(key), {});
if (value) {
return Promise.resolve(value);
} else {
return Promise.resolve(value);
}
}
// TODO
public getAllKeys(): Promise<string[]> {
const keys: any[] = [];
return Promise.resolve(keys);
}
private getKey(key: string) {
return `${this.prefix}${key}`;
}
}
I've been working on this recently and have a basic "working" example which isn't ready to be shared yet but I've found the main sticking point to be the use of local/session storage as this state isn't accessible on the server to obtain the access token, which you'll probably want to pass to your API or similar from both the client or the server.
If that's your need, then the design is wrong, IMO. This library is for apps that need protocol client-side. If you have server side code that also nerds protocol, then use something designed for your server. If you have a mixed app where you need both, then I'd tend to treat them as 2 separate clients.
Closing. If you still have issues, feel free to reopen.
Most helpful comment
I've been working on this recently and have a basic "working" example which isn't ready to be shared yet but I've found the main sticking point to be the use of local/session storage as this state isn't accessible on the server to obtain the access token, which you'll probably want to pass to your API or similar from both the client or the server.
If this is the only functionality you want on the server you could swap out the
userStoreimplementation to one that uses a cookie, I've not researched the security implications of this yet so be warned.So using next.js as an example you'll create a
userStorethat can optionally acceptNextContext, this includes the req and therefore the cookie, with the logic to use the request rather thandocument.cookiewhen on the server. I've included some example code at the bottom of this comment.If you'd also like to use the callback methods on the server then you'll hit a couple of other issues with both the redirect navigator, which can be replaced with a universal version via settings, unlike the
JsonServicewhich uses XMLHttpRequest that won't work on the server.@brockallen would you be interested in a PR to replace XMLHttpRequest with fetch which I believe is easier to polyfill on the server?
There are a few moving parts so to summarise to make the methods that aren't dependent on an iframe work you'll need to;
I'm sure there are other considerations which I've not found yet too.
Cooke State Store Example