From the documentation of serverless functions:
Using your Authenticated Serverless Function
As there is no login flow when using functions, the useRequireAuth
check assumes that your user is already authenticated and you have access to their JWT access token.
In your request, you must include the following headers:
- the auth provider type that your application is using, e.g.
dbAuth
- the Bearer token (JWT access token)
- if using dbAuth, then also the dbAuth Cookie
- is there an example of how to get these and then call the function from the web side?
const response = await fetch('http://localhost:8910/.redwood/functions/sendEmail', {
method: 'POST',
headers: {
'Authorization': 'Bearer myJWT.accesstoken.signature',
'auth-provider': 'dbAuth',
'Content-Type': 'application/json'
}
});
I am not sure if I need to send cookie because it already exists, and therefore the next question:
- Do I need to pass these even if i know that user is calling the function only when he/she is logged in? The
event
already has a header.cookie
.
I read that the recommended way is to rather spin a service and use gql query to access resources. But I am not accessing any resource, I want to just send an email to client who clicks a particular button.
Any help is appreciated.
What specifically are you trying to do?
I assume you’ve looked at the part of the docs that covers this: Serverless Functions (API Endpoints) | RedwoodJS Docs
It does sound like it may be easier for you to just use GraphQL to accomplish this — you don’t need to limit yourself to using GraphQL only for accessing resources, you could, for example, create a sendEmail
mutation that triggers your email sending business logic and then returns, say, a boolean indicating whether or not the sending of the email was successful.
If you do need to proceed outside of GraphQL for some reason, let me know, and I can help you figure out using auth in your custom serverless function. One thing you may be missing is that the Authorization token you should be sending is the one returned from useAuth
’s getToken
function. I believe you’re correct that you don’t need to manually send the cookie.
@arimendelow Thank you for a quick reply. I agree, in a prod version, I should probably have a GraphQL to accomplish this. But I want to finish a smaller PoC of a much larger app. Based on a certain login, I am just trying to send a quick email to user. I am hesitant of using GraphQL for the PoC because I am not sure if user is going to be logged in or not. My current version is going to focus on the assumption that user is logged in. But depending on how our discussion goes, I might have to quickly call a third party service for sending email from client, and in that case GraphQL won’t be necessary. In short, just trying to get functionality out there even though a bit of a dirty way.
I did try using getToken function as a hook, but it seems it returns a UUID and not really a JWT token.
import { useAuth } from 'src/auth';
const MyPage = () => {
const { getToken } = useAuth();
const handleButtonClick = async () => {
try {
const token = await getToken();
console.log('token: ', token);
This just prints the UUID: token: ee223ee7-f0d1-45ce-a665-439b9a9462ad
. I thought just making a call like
const response = await fetch('http://localhost:8910/.redwood/functions/sendEmail', {
method: 'POST',
headers: {
'Authorization': `Bearer {${token}}`,
'auth-provider': 'dbAuth',
}
});
Could be enough, but it seems the token I am getting is wrong.
In the Function, I am doing something simple, just send email from nodemailer, but I always get Access to sendEmailHandler was denied
. My code mostly looks like whats given in the docs, except my mail sending functionality. But the control never goes inside if (isAuthenticated()) {
block.
Please let me know if you need more information. I really appreciate your help.
What do you mean by this? You can use GraphQL even if the user isn’t logged in
You would use the @skipAuth
directive in your SDL. You can even do that, and then check auth status inside your service.
But if I am sending email through web part directly calling third party api, I wouldn’t want anything to do with the GraphQL / api side.
How is creating a dedicated serverless function better? It’s still part of your API side.
You can’t ever call a third party API directly from the client because it’s impossible to do so without exposing your API key. You always need to go through your server.
~better~ probably faster for me
We could be okay with exposing API key for the type user/app that we have. We are yet to discuss this. But for my PoC, I don’t want the GraphQL.
Again:
I agree, in a prod version, I should probably have a GraphQL to accomplish this. But I want to finish a smaller PoC of a much larger app. Based on a certain login, I am just trying to send a quick email to user. I am hesitant of using GraphQL for the PoC because I am not sure if user is going to be logged in or not. My current version is going to focus on the assumption that user is logged in. But depending on how our discussion goes, I might have to quickly call a third party service for sending email from client, and in that case GraphQL won’t be necessary. In short, just trying to get functionality out there even though a bit of a dirty way.
I use redwoodjs because it is supposed to give me speed.
I’m very confused why the user being logged in has anything to do with whether or not you want to use GraphQL.
Generally, I find that adding a new GraphQL mutation is significantly faster than getting a new serverless function up and running
However, if you’re okay with exposing your API key, just do the API call directly in the client!
Generally, my point is that unless you have a specific reason for wanting to use a single-purpose serverless function, I would caution you against it.
To clarify — if you’re deploying in serverless infrastructure, then any additional function you create will be deployed in the same way as the GraphQL one, just in its own Lambda. There are no infrastructure benefits to doing this, unless you have a specific need for it (size constraints, etc).
If you’re deploying in serverful infra, then it’s just deployed on your same server, and so there’s really no benefits AFAIK.
@arimendelow
Thank you for the reply. I already have read these things. Probably I am not clear enough, and all over the place, but actually re-iterating things from my first response only.
This is a PoC. I don’t know how to use GraphQL / mutations or anything else. Writing serverless functions is easier for me because I have done it in the past.
For now, based on my own judgement of what is better for me and our project, I want to use serverless functions.
If you know how what is wrong with this request:
import { useAuth } from 'src/auth';
const MyPage = () => {
const { getToken } = useAuth();
const handleButtonClick = async () => {
try {
const token = await getToken();
console.log('token: ', token);
const response = await fetch('http://localhost:8910/.redwood/functions/sendEmail', {
method: 'POST',
headers: {
'Authorization': `Bearer {${token}}`,
'auth-provider': 'dbAuth',
}
});
it would be beneficial to me and save me a couple of hours.
Thank you for your time.
Understood
Hmmmmm, first thing that stands out is that you’ve got a typo:
- 'Authorization': `Bearer {${token}}`,
+ 'Authorization': `Bearer ${token}`,
Additionally, you may need to add 'Content-Type': 'application/json'
to your headers.
@arimendelow Thank you very much for your help. I apologize for being a little rude to you in my last response.
I ended up simply passing the currentUser.id from web to the function in the request body. I guess if user is logged in, one would never need auth on the serverless function side.
Now I am moving ahead, but I don’t understand the use-case why useRequireAuth
is ever needed in serverless functions.
Because in that case, it is expected that user is already logged in, and web side has cookie and JWT token.
But this also means currentUser hook is available to get the user’s id, and passing that id to the function is possible, and easier for serverless function to get hold of the current user as well.
1 Like
No worries
Sounds good for an MVP, though be aware that if you deploy that, you’re essentially allowing for anyone on the internet to call your serverless function on behalf of any user. If you care about this, you need to additionally authenticate a user in your API.
I know this has a solution (kinda) but i am facing the same issue. getToken() supposedly should return the JWT token but returns the user id, i am missing something but seems misleading since i can’t really send the token to the api