Open dthyresson opened 3 years ago
@jtoar I was thinking to work on this as there is another Database Authentication coming after this PR, Should I continue or not ?
@dthyresson @thedavidprice
Hi @aggmoulik It's great having you as a contributor. Thanks for keeping it up!
For all intents and purposes, the dbAuth #2701 will be the Redwood "custom provider". So there's nothing more to be done here.
If you're looking for other high-priority issues that get us to v1 and need help now, talk a look at the column On deck (help-wanted)
here in the Current Release Sprint project board.
@dthyresson curious about your reaction to my thoughts above — in light of dbAuth provider, do you still think there's value in a boilerplate setup for "custom"?
Regardless, we should clear up the docs.
Not that you asked for my opinion, but until we have a more "plugin" approach to our auth providers I still think there would be value in having a "custom" generator. I think people are always going to want to add other third party auth providers, like maybe a direct Facebook auth solution. Or Twitch.
@dac09 Did you use a custom provider for a cli side?
I do have multiple custom auth ones, but because I use them outside redwood, I don't have a need for a provider, just some additional logic in getCurrentUser
in src/lib/auth.ts
@thedavidprice and @Tobbe I cannot think of a practical reason for a custom
provider generator -- and if anything it complicates the authentication docs.
The biggest issue is even if the api-side has some custom logic, what is the web side to do? You need a client that requests the AuthProvider and AuthContextInterface
(login, signUp, hasRole, currentUser, etc). Does that get "stubbed" out?
Some background, a year ago I tried to implement NetlifyOAuth as a new provider, rather than custom.
I abandoned it for reasons in that forum post, but can see in https://github.com/dthyresson/redwood/blob/dt-auth-client-netlify-oauth/packages/auth/src/authClients/netlifyOAuth.ts that you still need to implement that AuthContextInterface
specific to the auth method you intend to support.
The problem is that custom
the client and decoder is in the Redwood framework and maps when authenticating the request. But you cannot modify that code. That is you cannot map the "custom" auth type to some custom authentication code.
So, where does one actually implement the "custom" auth logic?
Hence I think why @dac09 has the logic inside:
I don't have a need for a provider, just some additional logic in getCurrentUser in src/lib/auth.ts
Because in getCurrentUser
one has access to the type (supabase, netlify ... custom) and if the type is custom, then you can "do something" with that request and determine if the request's user is authenticated.
Therefore, there the idea of a custom
generator setup isn't needed at all.
What should be documented is it you send a GraphQL request or any request really with the auth provider type of custom
, then how do you handle this in getCurrentUser?
I think this is much better solved via documentation and a cookbook example than a generator (because I don't think one can i fact be made).
I think this is much better solved via documentation and a cookbook example
This sounds good to me! Especially an example.
@thedavidprice Please see the above but TLDR; this issue is best served with an example or cookbook -- and also clarifying the documentation.
The custom provider is misleading because it really isn't declaring a new provider -- but rather that one case send the custom
auth type in a request and then have some custom logic in the getCurrentUser
auth method.
Perhaps @aggmoulik could create a new issue in the redwoodjs.com report to create a cookbook and update the docs -- after coming up with an example case.
Perhaps something that:
I think the header idea is best. Then getCurrentUser fetches the headers, looks for a value and decides if the authenticated or not.
But, I think we should close this in favor of docs.
It would great for @aggmoulik to have an understanding of auth for the community.
For me, a "custom" auth method has always mostly meant "auth provider we don't support yet". From your examples it sounds more like "totally different way of authing users". I think both are valid ways to look at it. But I wonder what the most common use-case is.
The Auth doc definitely needs clarification. I've created https://github.com/redwoodjs/redwoodjs.com/issues/740
I'm mixed on the ROI of a Cookbook given all the other priorities outstanding. It's a good idea, though. And might not be too much work given there's a lot of previous example code and Forum topics.
@aggmoulik I'll defer to you and your interest. And I agree there's a lot of need for more people to have deep knowledge about Redwood Auth!
Hey @thedavidprice @dthyresson It will be great, I will work on cookbook for authentication here and how can we setup the authentication rather than provided one on the frontend and backend in redwood application.
I will work on cookbook for authentication here
Thanks @aggmoulik even if it turns out that a generator is practical and helpful (I was rethinking things yesterday and maybe the real key to the generator isn't the api side, but rather the web-side to generate a client that implements the AuthProvider interface) and then a small lib in api that the getCurrentUser can use in lieu of a JWT decoder mapper class (that takes the event from the service request) actually doing a proof of concept and writing it up would help inform nay generator made.
TLDR; I think the exercise in writing the docs/how to cookbook will be helpful.
Please reach out to get with any questions and thanks again for taking on this task!
Hi all, just checking in here — is this still of interest and/or any help needed?
Yeah, I have been looking into it. I will work this weekend. So, I will let you know.
I am confused here a little bit as I should add a cookbook on setup custom JWT Provider or in a more general way, how to setup file structure?
I am confused here a little bit as I should add a cookbook on setup custom JWT Provider or in a more general way, how to setup file structure?
Cookbooks should be practical, real-world examples nit just sample docs.
The best thing to do would be ask 1) why do I need a custom auth provider? 2) how would that be used in a real-world app situation 3) describe why and for what purpose and then 4) show how the custom auth provider solves that problem.
It's as much why and it is how. And actually more specific than general. Ideally people could related to the problem and solution and adapt the solution in a new way that best suits them.
The only real-world case I could come up with was a JWT based token auth for a CLI -- but perhaps there are others.
For example, an "api token" auth where a user is given an api token/key and that is used to identify the user account or organization and grant permissions to access the api.
Just a reminder so we don't lose track of this:
The Auth doc definitely needs clarification. I've created redwoodjs/redwoodjs.com#740
We were just about to start on an LDAP auth integration (yeah we know it's almost way too sexy for 2022, but it's simply a requirement that we were given :shrug: :laughing:) and currently trying to piece the information together.
With https://github.com/redwoodjs/redwood/issues/5810 and currently no means to generate a custom template using the cli here, it is really a severe slowdown, if not showstopper to develop and contribute new auth providers to RedwoodJS.
Also it looks like the current auth providers are all web focused (except dbAuth and only judging from the few we tested so far), so we'd be grateful for hints regarding the formal process of adding auth to the api-side. Cheers.
Hi @Philzen just to let you know, the some of the Core Team (@Tobbe @dac09 @cannikin @callingmedic911 and me) met yesterday to discuss next steps to make it easier to implemented your own auth provider implementation for RedwoodJS.
As we get closer to those specs, we'll be in touch and perhaps you could be the first person to try out the new feature.
I made a note to check in with you, but please if you don't hear back from me in a few weeks or so, let me know.
I just pushed initial (untested) code for a custom auth setup command to my big auth refactor PR here: https://github.com/redwoodjs/redwood/pull/5985. But that PR only focuses on the web side. We'd also need to add some way to customize the api side, like adding a custom jwt decoder.
And as most other auth providers we have, it's focused on integrating with some auth SaaS.
@dthyresson @Tobbe @cannikin @callingmedic911
As we get closer to those specs, we'll be in touch and perhaps you could be the first person to try out the new feature.
I made a note to check in with you, but please if you don't hear back from me in a few weeks or so, let me know.
That sounds really awesome, thank you. We spoke to our project sponsor and were able to agree postponing the LDAP login until after our desperately needed summer hiatus. In the meantime they are happy with the dbAuth, which – thanks to Redwood's scaffolding awesomeness – we were able to rollout in a jiffy :smile:
So we'll be taking up again end of September / beginning of October.
Kindly advise what will be the best way to get in contact then.
Concerning the implementation itself, we did some sandbox experiments, and it turns out building the LDAP auth is no rocket science at all, and also it's one of the simplest use cases imaginable, as basically only login
-handler is required (as always, the biggest extra-work again comes due to Microsoft, which chose to roll their own standard concerning UserIDs in their AD implementation, but we think we already have a good solution to also cater for that).
I could see us implementing this in three steps:
Step 3. is a really crucial must-have for us, not only to enable peaceful coexistence with a local dev-setup, where we may not always have (or want) access to an AD behind a VPN, but we may not even get an account on such an AD but still need to be able to login to the deployed release to be able to support.
However i'm completely in the dark so far, how this could be done with the current handlers available. For 3.(a) i guess we could hook into the login-handler, but for 3.(b) we'd need be able to programmatically handle the login error, starting the ldapAuth-flow and on success create the record, then changing the error to a success (maybe sort-of abuse signup
-handler for both use cases?).
Just wanted to share this upfront so you could meditate about this.
I hope this makes sense to you and that i'm not asking to much here. On the other hand, i don't believe that we're the first people out there with such a requirement – however i couldn't find any examples on the Redwood Authentication docs that show how to use dbAuth in conjunction with other auth providers. The closest i found was the RBAC docs, which show how to manage the roles against an external user UUID … but then again i'm missing which handlers to hook into in order to seed them in the first place. I'm sure you all got it worked out already, but the docs could use some directions for Redwood noobs / ongoing-discoverers like me how this is intended – maybe even as a separate tutorial page, or another tutorial chapter?
If you like, i could provide a BPMN or such to illustrate the login process that we're after. Also, kindly advise where we should move this discussion for a more focussed effort. Cheers!
Kindly advise what will be the best way to get in contact then.
Both @dthyresson and I are very responsive if you ping us on Discord.
integrate the LDAP-Auth into the dbAuth-flow, having them coexist, where dbAuth is the main driver, delegating the authentication to LDAP when a user record either
I think we'd need to come up with a generic hook into the dbAuth flow for this to have any chance of being accepted 🙂 We definitely need to talk to @cannikin about this, and he's away on vacation for a week+, so can't do that right now.
On the other hand, i don't believe that we're the first people out there with such a requirement – however i couldn't find any examples on the Redwood Authentication docs that shows how to use dbAuth in conjunction with other auth providers.
Yeah, there are no docs, and no examples. It simply hasn't been done yet. But you're right, you're not the first to want this. There was some discussion here https://github.com/redwoodjs/redwood/pull/3337 Especially this comment
Yeah you bring up a good point. Now that I've learned how to use dbAuth, I'm thinking that I should completely re-haul the ETH Auth to piggyback on dbAuth. This is a lot, so I may need to move this to draft and come back to it
That was a while ago though, and I don't think anything else has come out of it.
If you like, i could provide a BPMN or such to illustrate the login process that we're after. Also, kindly advise where we should move this discussion for a more focussed effort. Cheers!
@Philzen thanks for offering to help work on this! Discord, as already mentioned, could be a way for us to collaborate. If that doesn't work for you, just ping me here when you're back from vacation and ready to start working on this and I'm sure @thedavidprice can help us figure out a way to efficiently collaborate.
During Redwood Office Hours yesterday, a "fake" auth provider was shared -- which they use in dev only to have offline auth and should never be used in production code -- but it is a nice short example of what it would take to implement a custom
Auth provider:
const FakeAuthProvider = ({ children }) => {
return (
<AuthContext.Provider
value={{
loading: false,
isAuthenticated: true,
userMetadata: { name: 'Fake User' },
currentUser: { sub: 'fake|1234', roles: ['admin'] },
hasError: false,
logIn: (options) =>
new Promise((resolve, _reason) =>
resolve(console.log('logIn', options))
),
logOut: (options) =>
new Promise((resolve, _reason) =>
resolve(console.log('logOut', options))
),
signUp: (options) =>
new Promise((resolve, _reason) =>
resolve(console.log('signUp', options))
),
getToken: () =>
new Promise((resolve, _reason) => {
console.log('getToken')
resolve('getToken')
}),
getCurrentUser: () =>
new Promise((resolve, _reason) => {
console.log('getCurrentUser')
resolve({ sub: 'fake|1234', roles: ['admin'] })
}),
hasRole: (rolesToCheck) => {
console.log('hasRole', rolesToCheck)
return true
},
reauthenticate: () =>
new Promise((resolve, _reason) => {
resolve(console.log('reauthenticate'))
}),
forgotPassword: (username) =>
new Promise((resolve, _reason) =>
resolve(console.log('forgotPassword', username))
),
resetPassword: (options) =>
new Promise((resolve, _reason) =>
resolve(console.log('resetPassword', options))
),
validateResetToken: (resetToken) =>
new Promise((resolve, _reason) =>
resolve(console.log('validateResetToken', resetToken))
),
client: {},
type: 'custom',
}}
>
{children}
</AuthContext.Provider>
)
}
const CustomAuthProvider = ({ children }) => {
if (process.env.FAKE_AUTH === 'true') {
return <FakeAuthProvider>{children}</FakeAuthProvider>
}
return <Auth0AuthProvider>{children}</Auth0AuthProvider>
}
// in api/src/lib/auth.ts
export const getCurrentUser = async (decoded): Promise<RedwoodUser> => {
if (process.env.FAKE_AUTH === 'true') {
decoded = { sub: 'fake|1234', roles: ['admin'] }
}
...
}
export const isAuthenticated = (): boolean => {
if (process.env.FAKE_AUTH === 'true') return true
return !!context.currentUser
}
export const hasRole = (roles: AllowedRoles): boolean => {
if (process.env.FAKE_AUTH === 'true') return true
}
See the following Community topic post from "edjiang":
While the docs do seem to indicate that one can generate a custom auth provider:
It is not supported because:
https://github.com/redwoodjs/redwood/blob/c46033a5229bdeba5d2549466e416400a22ba2b4/packages/cli/src/commands/generate/auth/auth.js#L35
and there is no "custom" template.
While it may be relatively easy to setup the web side, the api side is more complicated with the changes needed for graphql and the additional auth lib.
The custom generator could (and should) still add these to the api side, and stub on the web side.