Open davidjulakidze opened 1 year ago
Hello, @davidjulakidze 👋 and thanks for opening this issue. Is there a reason why you've got your Amplify.configure()
being called within the Home
component? While I don't think moving it outside your component will resolve the cookie issue, it should help to ensure that it's not being called on every render.
Calling Amplify.configure({...awsconfig, ssr:true})
enables SSR support within Amplify, but it doesn't handle the cookie management. Since you're using ssr: true
, it should be called on the server side. This might have been why you placed the configure call in your Home component, but the only way to ensure that it's only being called on the client side would be to call it within a useEffect hook.
Is there any particular reason why you're looking to manage cookies on the client side?
So to use amplify client side I would need to configure it using useEffect but how about if I want everything server side like so:
for example have an src/app/api set up with routes like: /configureAmplify <- Amplify.configure({}) /signIn <- SSR.Auth.signIn /getcurrentAuthenticatedUser <- SSR.Auth.currentAuthenticatedUser()
if I call /api/configureAmplify -> /api/signIn -> /api/getcurrentAuthenticatedUser I get "The user is not authenticated"
both signIn and getcurrentAuthenticatedUser amplify calls worked which means its configured correctly but yet getcurrentAuthenticatedUser returned user not authenticated even though signIn succeeded and returned a cognito user. why?
@davidjulakidze, you mentioned that both Auth.signIn()
and getCurrentAuthenticatedUser()
are working but you still see that error in the console. Can you share the frontend code that's calling the getCurrentAuthenticatedUser()
where you see "user is not authenticated" happening? Does it happen on refresh as well?
I'm attempting to do everything server side, lets say you have next.js api routes set up like so:
src/app/api/configureAmplify/route.ts
import { NextRequest, NextResponse } from "next/server";
import { Amplify } from "aws-amplify";
import awsconfig from "@/aws-exports";
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
Amplify.configure({ ...awsconfig, ssr: true });
return new NextResponse(
JSON.stringify({
message: "Amplify configured",
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
}
);
} catch (error) {
const { message } = error as { message: string };
const responseError = {
error: message,
};
return new NextResponse(JSON.stringify(responseError), {
status: 500,
headers: {
"Content-Type": "application/json",
},
});
}
}
src/app/api/signIn/route.ts
import { withSSRContext } from "aws-amplify";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
const { username, password } = await req.json();
const SSR = withSSRContext({ req });
const user = await SSR.Auth.signIn({
username,
password,
});
return new NextResponse(JSON.stringify(user), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
} catch (error) {
const { code, name, message, stack } = error as any;
const errorResponse = JSON.stringify({ code, name, message, stack });
return new NextResponse(errorResponse, {
status: 500,
headers: {
"Content-Type": "application/json",
},
});
}
}
src/app/api/getAuthenticatedUser/route.ts
import { withSSRContext } from "aws-amplify";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest): Promise<NextResponse> {
try {
const SSR = withSSRContext({ req });
const user = await SSR.Auth.currentAuthenticatedUser();
return new NextResponse(JSON.stringify(user), {
headers: { "content-type": "application/json" },
status: 200,
});
} catch (e) {
return new NextResponse(JSON.stringify(e), {
headers: { "content-type": "application/json" },
status: 500,
});
}
}
make the following api calls one after the other fetch("api/configureAmplify") -> fetch("api/signIn") -> fetch("api/getAuthenticatedUser")
fetch("api/configureAmplify") returns "amplify configured" fetch("api/signIn") returns a cognito user fetch("api/getAuthenticatedUser") returns not authenticated (?)
@davidjulakidze, appreciate you sharing the your code and providing some additional clarity. I think I can help address what you're looking to accomplish and possibly offer a suggestion to improve your app's security.
When using withSSRContext
, the request
object that is used is "read-only" for getting the cookies sent from the client. If you're looking to have cookies be populated on the client side, having ssr: true
configured will get you the cookies as long as you are calling the Auth API's on the client side. Currently, there is no way to refresh cookies or obtain new access tokens using the refresh token on the server side.
Additionally, the use of const { username, password } = await req.json();
does not look secure because you're extracting the username
and password
from the incoming request payload and sending them as JSON in the request body. Use of the Auth API's by contrast will always be encrypted with SRP.
Let me know if this provides the clarity you're looking for and helps determine next steps.
I have made changes to configure Amplify on both the server and client sides, moved the login logic to a server action, and attached the server action to my form. However, it seems there is still an error related to Amplify's configuration.
In the client component, I added the following code to configure Amplify on the client side:
"use client";
...
...
useEffect(() => {
Amplify.configure({ ...awsconfig, ssr: true });
}, []);
...
I moved the login logic to a server action:
"use server";
...
export async function login(formData: FormData): Promise<void> {
const username = formData.get("Username");
const password = formData.get("Password");
const SSR = withSSRContext();
const user = await SSR.Auth.signIn({
email,
password,
});
... (do stuff with user)
}
I attached the server action to my form in the client component:
<form action={login}>
...
</form>
` I make an api call to that configureAmplify api route mentioned above so that amplify is configured server side.
However, after filling out the form and pressing submit, I encountered the following error:
{"name":"NoUserPoolError","log":"\n Error: Amplify has not been configured correctly. \n The configuration object is missing required auth properties.\n This error is typically caused by one of the following scenarios:\n\n 1. Did you run
amplify pushafter adding auth via
amplify add auth?\n See https://aws-amplify.github.io/docs/js/authentication#amplify-project-setup for more information\n\n 2. This could also be caused by multiple conflicting versions of amplify packages, see (https://docs.amplify.aws/lib/troubleshooting/upgrading/q/platform/js) for help upgrading Amplify packages.\n "}
@cwomack Please let me know if I understand correctly: calling Auth.signIn()
on the server-side (i.e inside a server component/action) will NOT set the cookies?
@cwomack Please let me know if I understand correctly: calling
Auth.signIn()
on the server-side (i.e inside a server component/action) will NOT set the cookies?
from what i understand, authentication mutation actions should be done from client side. only data fetching should be from server, which can read the cookies, or API calls (which can be signed with the token already in cookies)
@cwomack Please let me know if I understand correctly: calling
Auth.signIn()
on the server-side (i.e inside a server component/action) will NOT set the cookies?from what i understand, authentication mutation actions should be done from client side. only data fetching should be from server, which can read the cookies, or API calls (which can be signed with the token already in cookies)
So I can't use a form action to sign my users in? This seems very redundant, especially since the latest next.js allows you to set cookies server side anyway
@affanshahid, that is correct. As @asp3 stated, any Auth API calls should be done on the client side. If you attempt to call the Auth API's on the server side (or within a server component), the cookies will not get set. This means the cookies will not be sent to the client-side browser.
@davidjulakidze, Next.js will indeed allow you to write outgoing request cookies in a server action, but it won't work with the recommended Authentication flows that Amplify Authentication and Cognito will use. The form actions should be done on the client side to collect the user inputs along with calling the Auth API's on the client side.
@cwomack alright thank you for your answers. assuming this is only for auth, would it be fair to assume we might see this become possible in the future versions of amplify-js?
@davidjulakidze, anytime! Given the specificity of this issue for something that isn't available (but desired) in the current version of Amplify, I'll actually convert this to a feature request and change the title. The ability for customers to be able to initiate Auth sessions and set cookies on the server side is something we may look into for a future version.
We appreciate you taking the time to open this issue! With it being converted to a feature request, more people may see this and give it some "thumbs up" or comments to help it gain traction.
I would really like to see this feature, but in our case for remix.
I have managed to get it working in a hacky fashion server side, but at the expense of the user agent and ip address being logged incorrectly.
I wanted to bump this topic now that v6 is out. I gave things a try today to see if I could get login working with a server action but it did not work. I suspect we can only do auth on the client side still.
@cwomack Can someone from the Amplify team please confirm if Next.js server actions are still unsupported in v6 for commands like signIn
, signUp
, etc?
From what I can tell, @aws-amplify/auth/server
only exports fetchAuthSession
, fetchUserAttributes
, and getCurrentUser
.
Edit: I see this was confirmed in a different issue: link
Hi @ryanwalters glad you were able to get a confirmation on the other issue. For additional reference here is the list of supported API for SSR usage:
Comment with additional context/usage - https://github.com/aws-amplify/amplify-js/issues/12660#issuecomment-1954899578
Hi @nadetastic , I want to use the amplify wrapper to interact with cognito because i'm familiar the amplify api. I need to be able to log users in and out, and send out invites for team members (multi tenant). I expect to write a lambda auth trigger to populate the tokens with tenant id.
Once I have an authenticated user I won't be passing the token back to AWS services beyond the remix server. I expect to build all the authentication in remix server-side.
At this stage in the product I just need basic auth functionality so i can get to the rest of the features.
I'm particularly interested in if this feature will allow Amplify to address known OAuth vulnerabilities to cross-site scripting attacks by turning the Amplify backend into a confidential client and a backend-for-frontend.
Before opening, please confirm:
JavaScript Framework
Next.js
Amplify APIs
Authentication
Amplify Categories
auth
Environment information
Describe the bug
I'm attempting to configure amplify in a client component then make a request to the server with the SSR context, but the cookies are not set after calling configure.
from official docs:
By providing ssr: true, Amplify persists credentials on the client in cookies so that subsequent requests to the server have access to them.
Expected behavior
cookies to be populated after Amplify.configure is called client side.
Reproduction steps
Code Snippet
src/app/page.tsx
Log output
aws-exports.js
Manual configuration
No response
Additional configuration
No response
Mobile Device
No response
Mobile Operating System
No response
Mobile Browser
No response
Mobile Browser Version
No response
Additional information and screenshots