Now that we have Envelop+Helix I went looking to the Envelop plugins repo - and there is nothing to help services manipulate cookies
How can my services read&write cookies
If not, why not?
Now that we have Envelop+Helix I went looking to the Envelop plugins repo - and there is nothing to help services manipulate cookies
How can my services read&write cookies
If not, why not?
I asked this question in the Envelop discussion area and they had some starting points: A Plugin To Manipulate Cookies? Ā· Discussion #1006 Ā· dotansimha/envelop Ā· GitHub
Based on that reply Iāve tried adding a plugin to the extraPlugins
part of the createGraphQLHandler
But I get no output to indicate that itās workingā¦
My useRequestHeadersPlugin
const path = require('path')
const __file = path.basename(__filename)
// https://www.npmjs.com/package/json-stringify-safe
const stringify = require('json-stringify-safe')
import { logger } from 'src/lib/logger'
export const useRequestHeadersPlugin = () => {
return {
onContextBuilding({ context, extendContext }) {
logger.debug(
`[${__file}] ~ onContextBuilding() context: ${stringify(
context,
null,
2
)}`
)
// extendContext({
// req: context.req,
// })
},
}
}
Using the extraPlugins
part of the createGraphQLHandler
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
import directives from 'src/directives/**/*.{js,ts}'
import sdls from 'src/graphql/**/*.sdl.{js,ts}'
import services from 'src/services/**/*.{js,ts}'
import { getCurrentUser } from 'src/lib/auth'
import { useRequestHeadersPlugin } from 'src/lib/envelop-request'
import { db } from 'src/lib/db'
import { logger } from 'src/lib/logger'
export const handler = createGraphQLHandler({
getCurrentUser,
loggerConfig: { logger, options: {} },
extraPlugins: [useRequestHeadersPlugin],
directives,
sdls,
services,
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()
},
})
Ah! I was not instantiating the plugin!
extraPlugins: [useRequestHeadersPlugin],
Should have been
extraPlugins: [useRequestHeadersPlugin()],
ok, so Iāve added request
to the context
but my getCurrentUser
in ./api/src/lib/auth.ts is not being called within those steps, and itās not being passed anythingā¦
api | DEBUG [2021-11-21 17:01:57.465 +0000]: [auth.js] ~ getCurrentUser args:
api | DEBUG [2021-11-21 17:01:57.466 +0000] (graphql-server): GraphQL execution started: __REDWOOD__AUTH_GET_CURRENT_USER
api | DEBUG [2021-11-21 17:01:57.467 +0000] (graphql-server): GraphQL execution completed: __REDWOOD__AUTH_GET_CURRENT_USER
api | POST /graphql 200 4.704 ms - 42
Also, my services are not receiving the ācontextā as expected from the Envelop plugin docsā¦
export const messaging = (args) => {
logger.debug(`[${__file}] ~ messaging args: ${stringify(args, null, 2)}`)
const { id } = args
return db.messaging.findUnique({
where: { id },
})
}
outputs:
api | DEBUG [2021-11-21 17:23:16.070 +0000] (graphql-server): GraphQL execution started: MessagingQuery
api | DEBUG [2021-11-21 17:23:16.077 +0000]: [messagings.js] ~ messaging args: {
api | "id": "to-register-phone"
api | }
api | DEBUG [2021-11-21 17:23:17.174 +0000] (graphql-server): GraphQL execution completed: MessagingQuery
Hi @ajoslin103 Iāve read this and the issue on the Envelop repo but could you explain more about what you want to do and why instead of how.
Why do you need to manipulate a cookie? And why would you want to do that within the GraphQL lifecycle?
Is this a different cookie than the dbAuth session cookie?
Also what do you want to store in a cookie?
Hi @dthyresson
As always, thanks for your time & attention !
I created a different kind of auth, one that uses the phone # as the username and a code [I just texted to that phone] as the password.
I had created it in 0.35 before the dbAuth showed up and I have to bring that code forward because it (for no reason I can figure out) wonāt deploy to Netlify anymore.
I wanted it brought up to date anyway.
Iāve been trying to shoehorn the two together for a few days (about 2 days longer than I should have.)
I was storing my validation token in browser local storage, and I got that into the cookie easily ā but my getCurrentUser isnāt being given anything. But Iām not using the .web side of the solutionā¦ So how could it work?
I just realized, while out leaf blowing, that I should just deploy dbAuth into a clean project and manipulate it for my own nefarious purposes
Then the next time around I might be able to contribute it as another auth scheme
Here we go!
Al;
-- redwood rules !!
Actually, as I work my way thru this -
When I send a code to someoneās phone Iāll want to encrypt it and store it into the hashedPasword so that it will match what is encrypted and sent down from the frontEnd (when the user enters the code into the login box)
So I will be hunting for the calls to encrypt the code before poking it into the hashedPassword column
Flow: User enters their phone & clicks forgot, I send them a code & encrypt that into the hashedPasword and then redirect them to the login w/a toast saying to enter the code in as the password, User then enters the code and hits Login
Could you diagram this flow? The actors, the devices, the data, the info exchange.
BTW - Supabase offers username/password auth and also āphoneā auth over SMS with a confirmation code.
Yes, Iāve seen that ā but Iām not 100% happy w/the supabase (or firebase) experience to date ā so they are now just my database.
This whole project has pivoted so many times itās not funny.
Iāll diagram it after it works ā l think I can do it all with the current set of dbAuth code/functionality
Afterwards we can decide how much to save if any, assuming my insanity can be curedā¦
Do you have the latest updates to dbAuth that include forgot/reset password functionality? In the forgotPassword.handler()
you could create a random password and put it in the hashedPassword
field, emailing it to them: Docs - Authentication : RedwoodJS Docs
Although with the default flow can you just use the built-in resetToken
method and let them set their own password when they come back to the site? Iām pretty sure that a security best-practice would be to make them change their password after you email them one in plain text, so if theyāre going to have to change it away, you could let dbAuth handle it all automatically.
Again all this is āhowā and still need to know the āwhyā and āwhatā. I ask only because I know some history here and there - if I recall - are two auth flows:
So, thereās this personal token sent to a third party that is used to look at (I think) Events to join.
Itās this second with flow that is cookie based ā not the genre use login. And thatās why I am not sure any Auth here is really needed and why I want to know more about the what and why.
If the second user simply views and event of does 2-3 few things, then a secure serverless function could work as well depending on the complexity of this personās interaction.
You could do a custom auth ā just one auth provider ā altogether and based on some header info, in the getCurrentUser decoded the token and either apply normal user auth flow or the custom flow where you verify the token.
Or you could add some audience claim the to JWT and one audience is app and audience is mobile and handle in getCurrentUser normally.
Lots of solutions - I could probably come up with 4-5 others to try and may or may not work - but first need to understand the purpose.
Apologies, as I was AFTK (try not to fall out of your chair, dear reader)
ā scene: a dim pub, many empties on the table - whence a plan was hatchedā¦
In the beginning, a few developer ages agoā¦ There was one app serving two populations, event planners and event attendeeās
And, strange but true: tāwas no UI for the attendeeās, they were neither a source of data, nor a consumerā¦ They were to opt in via text and that was pretty much that. It would cost them penniesā¦
ā scene: time passes, promises and negotiations, missed commitmentsā¦
So I built it, and yet they were not insideā¦ The whimsical winds of data availability had blown our dream away like so many cloudsā¦ So we had to pivot - the datasource promises had failed to fulfill. āOh Sorry, didnāt you get the memo?ā
ā scene: a brighter pub this time, fewer emptiesā¦
And lo, the attendee was deemed Responsible (via the incantation of legal texts, hereafter known as the āfine printā) and we could Certainly start relying on the attendeeās as their own datasource, which meant that they were to have logins and a UIā¦
ā scene: A quick devs year laterā¦ The spawn does live and talk (mostly spouting le finĆ© printe) and provide & consume data ( note: search for TLDR here [solved] AWS S3 - File uploads - #17 by ajoslin103 )
ā narrator: RW 0.38.3 and dbAuth are working great for the event planners, for whom an email address is 2nd nature - we resume with moreā¦
Aaaaannd so then the attendees had to have auth - and in the spirit of the original plan, that auth was to be as minimal as possible ā even an email address was too much
By this point the Event Planner and Attendee were sharing the same Supabase postgres database ā ( search for ārsyncā [Github PR in motion] Help, I need two apps with one database! )
So I decided to authenticate the attendees by texting them a passCode ā they would use their phone number as their username & enter the passCode as their password
I got that working in 0.35 but it was a little cumbersome and the code was not as clean as I would have liked - and then the netlify deploy broke itselfā¦ (wouldnāt deploy after not being touched for a 45 days, wtf, WtF !!)
ā scene: a lone dev making another fateful decision, then typing furiously for a week of daysā¦
I added hashedPassword, salt, resetToken & resetTokenExpiresAt to my validateByPhone
table
I named āphoneā as my username
So the attendee hits the Login page, and enters only their phone number
First I await forgotPassword(data.phone)
if that succeeds, then they exist and I can I kickoff the "sending of the code"
in the ./api
If that fails then I await signUp({ ...data, password: nanoid() })
ā note that the password is Irrelevant at this point, so I throw a guid at it for a fun obfuscation
That will succeed and I kickoff the "sending of the code"
in the ./api
The attendee is then redirected to the Signup Page, where their [unmodifiable] phone number is displayed, and they enter the ācode that they were sentā
And I await logIn({ ...data, username: phone })
If the code they send matches what is stored in the hashedPassword then they are considered logged in and all proceeds as normal.
scene: User Happy Path Concludes, Happily
ā epilogue: wherin lies the rub
āthe sending of the codeā
I generate a code
I create the hash & salt values using code lifted from AuthProvider's ./api Brother [DbAuthHandler] -- and store them in the table `validateByPhone` using the given phone number as the unique key
Then I text the code to the phone number in question
But, (and yes dear reader, after 45 years - it is a big But : )
It doesnāt workā¦ The salt and hashed values are somehow wrong?
What am I not understanding???
The Endā¦
(or is it?...
[thanks for your time and attention]
ā appendix a:
the lifted codeā¦
// hashes a password using either the given `salt` argument, or creates a new
// salt and hashes using that. Either way, returns an array with [hash, salt]
export const _hashPassword = (text: string, salt?: string) => {
const useSalt = salt || CryptoJS.lib.WordArray.random(128 / 8).toString()
return [
CryptoJS.PBKDF2(text, useSalt, { keySize: 256 / 32 }).toString(),
useSalt,
]
}
And so, it came to pass [the linter] and it was good
Ha Ha !!
Give it another whack and things DO WORK !!
Make that linter happy, finish the 0.38.3 updates ā finish some code I missedā¦
And weāre goldenā¦