-
Hello! This is likely a dumb question. I set up my routes and strategies per the readme using the GitHub strategy. I am able to "login" with GitHub, but when the callback route is invoked, I see:
GitHub seems to be hitting my route and providing
Am I supposed to be doing anything with those values manually? Any idea what might be causing this? expand to view code// app/services/auth.server.ts
import { Authenticator } from 'remix-auth';
import { SocialsProvider, GitHubStrategy } from 'remix-auth-socials';
import type { User } from '~/models/user.server';
import { sessionStorage } from '~/services/sessionStorage.server';
export const authenticator = new Authenticator<User>(sessionStorage, {
sessionKey: 'userId',
});
authenticator.use(
new GitHubStrategy({
clientID: '<REDACTED>',
clientSecret: '<REDACTED>',
callbackURL: `http://localhost:3000/auth/${SocialsProvider.GITHUB}/callback`,
}, async (params) => {
const user = getOrCreateUserByEmail(params.profile.emails);
return user;
})
); // app/services/sessionStorage.server.ts
import { createCookieSessionStorage } from "@remix-run/node";
import invariant from "tiny-invariant";
invariant(process.env.SESSION_SECRET, "SESSION_SECRET must be set");
export const sessionStorage = createCookieSessionStorage({
cookie: {
name: '__session',
httpOnly: true,
maxAge: 0,
path: "/",
sameSite: "lax",
secrets: [process.env.SESSION_SECRET],
secure: process.env.NODE_ENV === "production",
},
}); // app/routes/auth/$provider.ts
import type { ActionFunction, LoaderFunction } from "@remix-run/node";
import { redirect } from "@remix-run/node";
import invariant from "tiny-invariant";
import { authenticator } from "~/services/auth.server";
export const loader: LoaderFunction = () => redirect("/login");
export const action: ActionFunction = ({ request, params }) => {
invariant(params.provider, "Must specify a provider.");
return authenticator.authenticate(params.provider, request);
}; // app/routes/auth/$provider.callback.tsx
import type { LoaderFunction } from "@remix-run/node";
import invariant from "tiny-invariant";
import { authenticator } from "~/services/auth.server";
export const loader: LoaderFunction = async ({ request, params }) => {
invariant(params.provider, "Must specify a provider.");
return authenticator.authenticate(params.provider, request, {
successRedirect: "/dashboard",
failureRedirect: "/login",
});
}; // app/routes/login.tsx
export default functon LoginPage() {
return (
<div>
<Form action={`/auth/github`} method="post">
<button>Log in with GitHub</button>
</Form>
</div>
)
} |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 10 replies
-
This usually happens when there’s no session cookie set, which usually happens because of a misconfigured session cookie. Most of your code looks fine except the maxAge 0, that is telling the browser the cookie max age is 0 seconds so it’s immediately stale, try removing that or using a larger value like a day. If you remove it the cookie will live until the user closes the browser. |
Beta Was this translation helpful? Give feedback.
-
Are there any other reasons that users might see this error? I just launched a project and a small portion of the user base is seeing this error when they try to authenticate. I am using the GoogleStrategy so I'm not sure if that is having any impact. Here is my code: import dotenv from "dotenv";
import { createCookieSessionStorage } from "@remix-run/node";
import { Authenticator } from "remix-auth";
import { GoogleStrategy } from "remix-auth-google";
import { getUser } from "./users";
dotenv.config();
if (!process.env.GOOGLE_CLIENT_ID) {
throw new Error("GOOGLE_CLIENT_ID is required");
}
if (!process.env.GOOGLE_CLIENT_SECRET) {
throw new Error("GOOGLE_CLIENT_SECRET is required");
}
if (!process.env.COOKIE_SECRET) {
throw new Error("COOKIE_SECRET is required");
}
export const sessionStorage = createCookieSessionStorage({
cookie: {
name: "__session",
httpOnly: true,
path: "/",
sameSite: "lax",
secrets: [process.env.COOKIE_SECRET],
secure: process.env.NODE_ENV === "production",
},
});
export const auth = new Authenticator(sessionStorage);
const callbackURL =
process.env.NODE_ENV === "production"
? "https://my-hosted-app.com/auth/google/callback"
: "http://localhost:3000/auth/google/callback";
auth.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
callbackURL,
},
async ({ profile, accessToken, extraParams }) => {
const user = await getUser(profile._json.sub, { _id: profile._json.sub });
if (!user) throw "missing user";
return {
_id: user.id,
name: user.name,
email: user.email,
picture: user.picture,
roles: user.roles,
lastSeenSnap: user.lastSeenSnap,
};
}
)
); I have tried having the user clear cookies for the site thinking there may be some information in there from previous versions but that did not work either. Any suggestions would be really helpful. Thanks |
Beta Was this translation helpful? Give feedback.
-
As a slight addendum to the fix from @sergiodxa mentioned above, we ran into this problem intermittently (perhaps one in every 10-20 logins) when using Chrome. In our case we didn't have expires or maxage set at all (assuming we'd get browser "session" mechanics for the cookie):
The oddity was that in the case it failed, we were in fact sending set-cookie in step 2, but Chrome would not send it again in step 4. It would otherwise work perfectly fine. Explicitly setting an
I'm unsure if this is a Chrome bug, seems to be odd behavior. TLDR; Always set expires or maxage |
Beta Was this translation helpful? Give feedback.
-
I also have this problem, the problem arrises when using the Apple auth flow. The callback from apple is a POST call and not a GET call (with query string parameters). The callback call is an HTTP POST request containing the results of the authorization is sent to the redirectURI. As described over here: https://developer.apple.com/documentation/sign_in_with_apple/request_an_authorization_to_the_sign_in_with_apple_server When the POST call is made I cannot read the session cookie. I tried it when setting the response_mode to query then it works ok since the cllback call is a GET call with query strings. But I need to have the id_token returned as well and that only works when the response_mode is set to form_post. |
Beta Was this translation helpful? Give feedback.
This usually happens when there’s no session cookie set, which usually happens because of a misconfigured session cookie.
Most of your code looks fine except the maxAge 0, that is telling the browser the cookie max age is 0 seconds so it’s immediately stale, try removing that or using a larger value like a day.
If you remove it the cookie will live until the user closes the browser.