The magic is in creating the AuthClient
in authClients
where the actual typing for the specific wrapping of the vendor client is lost.
In AuthProvider
, methods on AuthClient
are being called, and the vendor client itself, with the specific AuthClient
is exposed, that, by itself seems very much OK to me.
However, by having to pass down the AuthClient
(for custom) OR vendor client
+ type
into AuthProvider
and even createAuthClient
, type safety is lost, and it’s rather non-trivial to get it back. The login | signUp | getUserMetadata | etc.
inputs and returns can’t be easily defined.
One way to remedy this issue with only type change would be to trust the user with providing generic into useAuth
, as it doesn’t really matter if the AuthProvider
is more loosely typed.
The proposal is extracting and exporting the createAuthClient
logic, so AuthProvider
can use and surface the real AuthClient
, even if it’s custom, making AuthClient
properly typed, and keep the vendor client wrappers, and their relevant types (that Redwood, or any 3rd party provides) separate from the main package. This would enable AuthProvider
to type rwClient
as <AppAuthClient extends AuthClient>
and bind it to the client
property, so the types are more visible, and work inside the AuthProvider
can be safer with unknown
s instead of any
s
It would also help establish the other creators’ role as simple wrappers that are provided by RW, and "feel free to change or shape however you see fit as long as the return type extends AuthClient
"
import { AuthProvider, createAuth0Client, RwAuth0Client } from '@redwoodjs/auth'
import { Auth0Client } from '@auth0/auth0-spa-js'
const auth0 = new Auth0Client({
domain: process.env.AUTH0_DOMAIN,
client_id: process.env.AUTH0_CLIENT_ID,
redirect_uri: 'http://localhost:8910/',
cacheLocation: 'localstorage',
audience: process.env.AUTH0_AUDIENCE,
})
interface MyAuth0Client extends {
foo(): Promise<void>
}
function createMyAuth0Client(client: Auth0Client): MyAuth0Client {
const baseClient = createAuth0Client(client)
return {
...baseClient,
wipeData() {
return Promise.all([client.foo(), client.bar()])
}
}
const authClient = createMyAuth0Client(auth0)
ReactDOM.render(
<AuthProvider client={createAuth0Client(auth0)}>
)
const auth = useAuth<RwAuth0Client>()
Going further on the change, a typing extension could also be made for AuthClient
, so that useAuth
would also be type-safe even without passing any generics.
declare module '@redwoodjs/auth' {
export interface AuthClient extends RwAuth0Client {}
}
Sorry, this might be a bit all over the place, let me know if any more clarification would be beneficial.
ps.
Another option that may be worth considering, but may limit the flexibility of auth
module too much, is to use a creator, passing the AuthClient
, yielding const { AuthProvider, useAuth } = createRwAuth(createAuth0Client(auth0))