Get current domain name from request in Nuxt - apache2

How can I get the current domain name from a request on server side?
My Nuxt based website is reachable from different domains. I would like to get the domain name from where the user has accessed the website. How can I do this?
I tried to write a middleware for that purpose but it always displays localhost:3000
export default function({ store, req }) {
if (process.server) store.commit('hostname', req.headers.host)
}
Any idea how to fix this issue?

If you are using Vuex, you can use the nuxtServerInit action to access the request headers.
In my example, I am storing the domain in Vuex so it can be accessed anywhere in the application because this middleware fires before all other ones.
store/index.js
export const state = () => ({
domain: '',
});
export const mutations = {
setDomain(state, domain) {
state.domain = domain;
},
};
export const actions = {
nuxtServerInit(store, context) {
store.commit('setDomain', context.req.headers.host);
},
};
export const getters = {
domain: (state) => state.domain,
};
middleware/domain.js
export default function ({ route, store, redirect }) {
console.log('hey look at that', store.getters['domain']);
}
nuxt.config.js
export default {
...
router: {
middleware: 'domain'
},
}

I'm able to get the domain name declaring the function on the server by this way:
export default function (req, res, next) {
console.log(req.headers.host)
}
If you need to commit the data to the store, saving data in the environment and recover it on the client side could be a solution.

Related

How to handle sessions securely in SvelteKit

I'm fairly new to SvelteKit and I'm wondering how to properly handle sessions. From what I understand a unique session ID is typically stored in the browser as a cookie and this is linked to the currently signed in user backend. I'm used to PHP where all this is taken care of for you but that doesn't seem to be the case with SvelteKit.
This is what I've got so far:
/src/hooks.ts:
import cookie from 'cookie';
import { v4 as uuid } from '#lukeed/uuid';
import type { Handle } from '#sveltejs/kit';
import { getOrCreateSession } from '$lib/Authentication';
export const handle: Handle = async ({ event, resolve }) => {
const cookies = cookie.parse(event.request.headers.get('cookie') || '');
event.locals.userid = cookies.userid || uuid();
const response = await resolve(event);
if (!cookies.userid) {
// if this is the first time the user has visited this app,
// set a cookie so that we recognise them when they return
response.headers.set(
'set-cookie',
cookie.serialize('userid', event.locals.userid, {
path: '/',
httpOnly: true
})
);
}
return response;
};
export async function getSession(event) : Promise<App.Session> {
return getOrCreateSession(event.locals.userid);
}
/src/app.d.ts:
/// <reference types="#sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#the-app-namespace
// for information about these interfaces
declare namespace App {
interface Locals {
userid: string;
}
// interface Platform {}
interface Session {
id: string;
userId?: number;
}
// interface Stuff {}
}
/src/lib/Authentication.ts:
export let sessions: App.Session[] = [];
export async function getOrCreateSession(id: string) {
let session = sessions.find(x => x.id === id);
if(!session) {
session = {
id: id,
};
sessions.push(session);
}
return session;
}
/src/routes/setSession.svelte:
<script lang="ts" context="module">
export async function load({ session }) {
session.userId = 1;
return {}
}
</script>
/src/routes/getSession.svelte:
<script lang="ts" context="module">
export async function load({ fetch, session }) {
return {
props: {
session: session,
}
}
}
</script>
<script lang="ts">
export let session: App.Session;
</script>
{JSON.stringify(session)}
Visiting /setSession will set userId and it'll be retrieved when visiting /getSession.
Is this the proper way to do this?
Other than sessions not persisting on server restart, is there any other drawback storing the sessions in a variable instead of a database?
Over time sessions will become quite large. To have an expiry date that's extended on each request would probably be a good idea but where would be a good place to put the code that remove all expired sessions?
I am also new to sveltekit and I believe that sveltekit server side is typically stateless so that it can be deployed on edge functions.
One way to manage sessions, would be to store all session data in an HTTP only cookie (not only the session ID). This is the idea of svelte-kit-cookie-session
Of course, you can also use a database to store session data.
To manage expiration:
If you store session data in a cookie, then set the expires parameter on the creation
If you store session data in a database, depending on the database, you might use a Time To Live policy...

Logout from next-auth with keycloak provider not works

I have a nextjs application with next-auth to manage the authentication.
Here my configuration
....
export default NextAuth({
// Configure one or more authentication providers
providers: [
KeycloakProvider({
id: 'my-keycloack-2',
name: 'my-keycloack-2',
clientId: process.env.NEXTAUTH_CLIENT_ID,
clientSecret: process.env.NEXTAUTH_CLIENT_SECRET,
issuer: process.env.NEXTAUTH_CLIENT_ISSUER,
profile: (profile) => ({
...profile,
id: profile.sub
})
})
],
....
Authentication works as expected, but when i try to logout using the next-auth signOut function it doesn't works. Next-auth session is destroyed but keycloak mantain his session.
After some research i found a reddit conversation https://www.reddit.com/r/nextjs/comments/redv1r/nextauth_signout_does_not_end_keycloak_session/ that describe the same problem.
Here my solution.
I write a custom function to logout
const logout = async (): Promise<void> => {
const {
data: { path }
} = await axios.get('/api/auth/logout');
await signOut({ redirect: false });
window.location.href = path;
};
And i define an api path to obtain the path to destroy the session on keycloak /api/auth/logout
export default (req, res) => {
const path = `${process.env.NEXTAUTH_CLIENT_ISSUER}/protocol/openid-connect/logout?
redirect_uri=${encodeURIComponent(process.env.NEXTAUTH_URL)}`;
res.status(200).json({ path });
};
UPDATE
In the latest versions of keycloak (at time of this post update is 19.*.* -> https://github.com/keycloak/keycloak-documentation/blob/main/securing_apps/topics/oidc/java/logout.adoc) the redirect uri becomes a bit more complex
export default (req, res) => {
const session = await getSession({ req });
let path = `${process.env.NEXTAUTH_CLIENT_ISSUER}/protocol/openid-connect/logout?
post_logout_redirect_uri=${encodeURIComponent(process.env.NEXTAUTH_URL)}`;
if(session?.id_token) {
path = path + `&id_token_hint=${session.id_token}`
} else {
path = path + `&client_id=${process.env.NEXTAUTH_CLIENT_ID}`
}
res.status(200).json({ path });
};
Note that you need to include either the client_id or id_token_hint parameter in case that post_logout_redirect_uri is included.
So, I had a slightly different approach building upon this thread here.
I didn't really like all the redirects happening in my application, nor did I like adding a new endpoint to my application just for dealing with the "post-logout handshake"
Instead, I added the id_token directly into the initial JWT token generated, then attached a method called doFinalSignoutHandshake to the events.signOut which automatically performs a GET request to the keycloak service endpoint and terminates the session on behalf of the user.
This technique allows me to maintain all of the current flows in the application and still use the standard signOut method exposed by next-auth without any special customizations on the front-end.
This is written in typescript, so I extended the JWT definition to include the new values (shouldn't be necessary in vanilla JS
// exists under /types/next-auth.d.ts in your project
// Typescript will merge the definitions in most
// editors
declare module "next-auth/jwt" {
interface JWT {
provider: string;
id_token: string;
}
}
Following is my implementation of /pages/api/[...nextauth.ts]
import axios, { AxiosError } from "axios";
import NextAuth from "next-auth";
import { JWT } from "next-auth/jwt";
import KeycloakProvider from "next-auth/providers/keycloak";
// I defined this outside of the initial setup so
// that I wouldn't need to keep copying the
// process.env.KEYCLOAK_* values everywhere
const keycloak = KeycloakProvider({
clientId: process.env.KEYCLOAK_CLIENT_ID,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
issuer: process.env.KEYCLOAK_ISSUER,
});
// this performs the final handshake for the keycloak
// provider, the way it's written could also potentially
// perform the action for other providers as well
async function doFinalSignoutHandshake(jwt: JWT) {
const { provider, id_token } = jwt;
if (provider == keycloak.id) {
try {
// Add the id_token_hint to the query string
const params = new URLSearchParams();
params.append('id_token_hint', id_token);
const { status, statusText } = await axios.get(`${keycloak.options.issuer}/protocol/openid-connect/logout?${params.toString()}`);
// The response body should contain a confirmation that the user has been logged out
console.log("Completed post-logout handshake", status, statusText);
}
catch (e: any) {
console.error("Unable to perform post-logout handshake", (e as AxiosError)?.code || e)
}
}
}
export default NextAuth({
secret: process.env.NEXTAUTH_SECRET,
providers: [
keycloak
],
callbacks: {
jwt: async ({ token, user, account, profile, isNewUser }) => {
if (account) {
// copy the expiry from the original keycloak token
// overrides the settings in NextAuth.session
token.exp = account.expires_at;
token.id_token = account.id_token;
}
return token;
}
},
events: {
signOut: ({ session, token }) => doFinalSignoutHandshake(token)
}
});
signOut only clears session cookies without destroying user's session on the provider.
Year 2023 Solution:
hit GET /logout endpoint of the provider to destroy user's session
do signOut() to clear session cookies, only if step 1 was successful
Implementation:
Assumption: you are storing user's idToken in the session object returned by useSession/getSession/getServerSession
create an idempotent endpoint (PUT) on server side to make this GET call to the provider
create file: pages/api/auth/signoutprovider.js
import { authOptions } from "./[...nextauth]";
import { getServerSession } from "next-auth";
export default async function signOutProvider(req, res) {
if (req.method === "PUT") {
const session = await getServerSession(req, res, authOptions);
if (session?.idToken) {
try {
// destroy user's session on the provider
await axios.get("<your-issuer>/protocol/openid-connect/logout", { params: id_token_hint: session.idToken });
res.status(200).json(null);
}
catch (error) {
res.status(500).json(null);
}
} else {
// if user is not signed in, give 200
res.status(200).json(null);
}
}
}
wrap signOut by a function, use this function to sign a user out throughout your app
import { signOut } from "next-auth/react";
export async function theRealSignOut(args) {
try {
await axios.put("/api/auth/signoutprovider", null);
// signOut only if PUT was successful
return await signOut(args);
} catch (error) {
// <show some notification to user asking to retry signout>
throw error;
}
}
Note: theRealSignOut can be used on client side only as it is using signOut internally.
Keycloak docs logout

Next.js server side props not loading in time

I'm using supabase and trying to load the user session on the server side. If you refresh the page, it catches there is a user but not on first load (e.g. like when coming from a magic link). How can I ensure it does load before he page?
List item
Here is the page:
import router from "next/router";
import { supabase } from "../utils/supabaseClient";
function Home() {
const user = supabase.auth.user()
if (user){
//router.push('/admin') failsafe, not ideal
}
return (
<div className="min-h-screen bg-elkblue dark:bg-dark-pri">
marketing
</div>
);
}
export async function getServerSideProps({ req }) {
const { user } = await supabase.auth.api.getUserByCookie(req);
if (user) {
return {
redirect: {
destination: "/admin",
permanent: false,
},
};
}
return {
props: { }, // will be passed to the page component as props
};
}
export default Home;
You can use the auth-helpers for help with server-side rendering https://github.com/supabase-community/supabase-auth-helpers/blob/main/src/nextjs/README.md#server-side-rendering-ssr---withpageauth
Do note that it however needs to render the client first after OAuth because the server can't access the token from the URL fragment. The client will then read the token from the fragment and forward it to the server to set a cookie which can then be used for SSR.
You can see an example of that in action here: https://github.com/vercel/nextjs-subscription-payments/blob/main/pages/signin.tsx#L58-L62

How to properly hook up Apollo Client to Redux in React Native

I've been researching this for the past couple hours, and I am now extremely close to solving this. Everything seems to be working now, but I do not have this.props.mutate in my component that has the Apollo HOC wrapping it.
I wired Apollo up as a reducer, so in my component, I would expect to see this.props.apollo.mutate available, but it's not there.
This is all that seems to be provided currently:
console.log(this.props.apollo)
{"data":{},"optimistic":[],"reducerError":null}
Here is how it is hooked up:
./src/apolloClient.js
import { AsyncStorage } from 'react-native'
import { ApolloClient, createNetworkInterface } from 'react-apollo'
const networkInterface = createNetworkInterface({
uri: 'http://localhost:8000/graphql',
opts: {
credentials: 'same-origin',
mode: 'cors'
}
})
// networkInterface is half baked as I haven't got this far yet
networkInterface.use([{
async applyMiddleware(req, next) {
if (!req.options.headers) req.options.headers = {}
const token = await AsyncStorage.getItem('token')
req.options.headers.authorization = token ? token : null
next()
}
}])
const client = new ApolloClient({
networkInterface,
dataIdFromObject: (o) => o.id
})
export default client
./src/reducers.js
import client from './apolloClient'
export default combineReducers({
apollo: client.reducer(),
nav: navReducer,
signup: signupReducer,
auth: loginReducer,
})
./src/App.js
import store from './store'
import client from './apolloClient'
const Root = () => {
return (
<ApolloProvider store={store} client={client}>
<RootNavigationStack />
</ApolloProvider>
)
}
export default Root
Oh, and here's the bottom of my Login component (also fairly half-baked):
export default compose(
connect(mapStateToProps, {
initiateLogin,
toggleRememberMe
}),
withFormik({
validate,
validateOnBlur: true,
validateOnChange: true,
handleSubmit: ({ tel, password }, { props }) => {
props.apollo.mutate({
variables: { tel, password }
})
.then((res) => {
alert(JSON.stringify(res))
//const token = res.data.login_mutation.token
//this.props.signinUser(token)
//client.resetStore()
})
.catch((err) => {
alert(JSON.stringify(err))
//this.props.authError(err)
})
//props.initiateLogin({ tel, password })
}
}),
graphql(LOGIN_MUTATION, { options: { fetchPolicy: 'network-only' } })
)(LoginForm)
It feels like I need an action creator and to manually map it to my component. What do I need to do to run this loosely shown mutation LOGIN_MUTATION onSubmit?
I'm currently confused by the fact this.props.apollo has Apollo's data in it, but there is no mutate.
I don't see the solution here: http://dev.apollodata.com/react/mutations.html or maybe I do -- is this what I need to be looking at?
const NewEntryWithData = graphql(submitRepository, {
props: ({ mutate }) => ({
submit: (repoFullName) => mutate({ variables: { repoFullName } }),
}),
})(NewEntry)
I'd like to get it to the point where the component can call the mutation when it needs to. I'd also like it to be available on this.props.something so I can call it from Formik's handleSubmit function, but I am open to suggestions that enable the best declarative scalability.
[edit] Here is the code that I am considering solved:
./src/apolloClient.js
This file was scrapped.
./src/reducers.js
I removed the Apollo reducer and client reference.
./src/App.js
I put the Apollo Client inside the root component. I got this technique from Nader Dabit's Medium post. He illustrates this in a GitHub repo:
https://github.com/react-native-training/apollo-graphql-mongodb-react-native
https://medium.com/react-native-training/react-native-with-apollo-part-2-apollo-client-8b4ad4915cf5
Here is how it looks implemented:
const Root = () => {
const networkInterface = createNetworkInterface({
uri: 'http://localhost:8000/graphql',
opts: {
credentials: 'same-origin',
mode: 'cors'
}
})
networkInterface.use([{
async applyMiddleware(req, next) {
try {
if (!req.options.headers) req.options.headers = {}
const token = await AsyncStorage.getItem('token')
req.options.headers.authorization = token || null
next()
} catch (error) {
next()
}
}
}])
const client = new ApolloClient({
networkInterface,
dataIdFromObject: (o) => o.id
})
return (
<ApolloProvider store={store} client={client}>
<RootNavigationStack />
</ApolloProvider>
)
}
export default Root
When you use compose, the order of your HOCs matters. In your code, the props added by your first HOC (connect) are available to all the HOCs after it (withFormik and graphql). The props added by withFormik are only available to graphql. The props added by graphql are available to neither of the other two HOCs (just the component itself).
If you rearrange the order to be compose -> graphql -> withFormik then you should have access to props.mutate inside withFormik.
Additionally, while you can integrate Redux and Apollo, all this does is prevent you from having two separate stores. Passing an existing store to Apollo is not going to change the API for the graphql HOC. That means, regardless of what store you're using, when you correctly use the HOC, you will still get a data prop (or a mutate prop for mutations).
While integrating Apollo with Redux does expose Apollo's store to your application, you should still use Apollo like normal. In most cases, that means using the graphql HOC and utilizing data.props and data.mutate (or whatever you call those props if you pass in a name through options).
If you need to call the Apollo client directly, then use withApollo instead -- this exposes a client prop that you can then use. The apollo prop that connect exposes in your code is just the store used by Apollo -- it's not the actual client, so it will not have methods like mutate available to it. In most cases, though, there's no reason to go with withApollo over graphql.

How to preload data with react-router v4?

This is example from official docs (https://reacttraining.com/react-router/web/guides/server-rendering/data-loading):
import { matchPath } from 'react-router-dom'
// inside a request
const promises = []
// use `some` to imitate `<Switch>` behavior of selecting only
// the first to match
routes.some(route => {
// use `matchPath` here
const match = matchPath(req.url, route)
if (match)
promises.push(route.loadData(match))
return match
})
Promise.all(promises).then(data => {
// do something w/ the data so the client
// can access it then render the app
})
This documentation makes me very nervous. This code doesn't work. And this aproach doesn't work! How can I preload data in server?
This Is what I have done - which is something I came up with from the docs.
routes.cfg.js
First setup the routes config in a way that can be used for the client-side app and exported to used on the server too.
export const getRoutesConfig = () => [
{
name: 'homepage',
exact: true,
path: '/',
component: Dasboard
},
{
name: 'game',
path: '/game/',
component: Game
}
];
...
// loop through config to create <Routes>
Server
Setup the server routes to consume the config above and inspect components that have a property called needs (call this what you like, maybe ssrData or whatever).
// function to setup getting data based on routes + url being hit
async function getRouteData(routesArray, url, dispatch) {
const needs = [];
routesArray
.filter((route) => route.component.needs)
.forEach((route) => {
const match = matchPath(url, { path: route.path, exact: true, strict: false });
if (match) {
route.component.needs.forEach((need) => {
const result = need(match.params);
needs.push(dispatch(result));
});
}
});
return Promise.all(needs);
}
....
// call above function from within server using req / ctx object
const store = configureStore();
const routesArray = getRoutesConfig();
await getRouteData(routesArray, ctx.request.url, store.dispatch);
const initialState = store.getState();
container.js/component.jsx
Setup the data fetching for the component. Ensure you add the needs array as a property.
import { connect } from 'react-redux';
import Dashboard from '../../components/Dashboard/Dashboard';
import { fetchCreditReport } from '../../actions';
function mapStateToProps(state) {
return { ...state.creditReport };
}
const WrappedComponent = connect(
mapStateToProps,
{ fetchCreditReport }
)(Dashboard);
WrappedComponent.needs = [fetchCreditReport];
export default WrappedComponent;
Just a note, this method works for components hooked into a matching routes, not nested components. But for me this has always been fine. The component at route level does the data fetch, then the components that need it later either has it passed to them or you add a connector to get the data direct from the store.

Resources