Auth
Authentication
The authentication process uses the external service Auth0, this couples perfectly with the microservices architecture of the project, giving out-of-the-box traditional Email+Password authentication, and enables easier integration with social connections, like Google / Gmail and Microsoft Accounts, which are the currently enabled connections, and with further subscription plans, has the potential for integration with Google Workspace and Office 365.
Auth0
One of the best advantages of using Auth0 is that it makes its usage easier for both client-side and server-side.
Client side
For client-side, and more specifically in learner-model-gql-template, the project uses the auth0-react library, the source code, and lower-level documentation is available in GitHub, meanwhile, the higher level documentation is available in the Auth0 Website.
Other than configuring the Auth0 integration in manage.auth0.com, to re-use the existing integration already created and available, a couple of basic configurations on the client-side of the project have to be done, for example, setting, for example, the following environment variables:
NEXT_PUBLIC_AUTH0_DOMAIN=learner-model-gql.us.auth0.com
NEXT_PUBLIC_AUTH0_CLIENT_ID=RRfBLJvPlx2R6opfhYqGSb23oh7vZUFx
And the Auth0 Provider of auth0-react as it follows:
<Auth0Provider domain={process.env.NEXT_PUBLIC_AUTH0_DOMAIN!} clientId={process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID!} redirectUri={ typeof window !== "undefined" ? window.location.origin : undefined }> ...</Auth0Provider>
The service expects the raw token in the authorization
header, with the prefix Bearer
, something like:
({ headers: { authorization: `Bearer ${token}`, },});
Server side
On the server-side, the services use the plugin fastify-auth0-verify, which
given the AUTH0_DOMAIN
, AUTH0_CLIENT
and AUTH0_SECRET
secret environment variables, it decodes the authorization token provided in the authorization
header,
and the services can identify the authenticated user.
Authorization
The authorization process relies on the local database user modeling, by using the "email" field provided by Auth0 and connecting the Auth0's uid to a local database user.
After the services have access to the "user" entity, they check the user's projects (including projects of the user's groups) compared to the requested project's data.
The majority of the logic of the authorization rules are shared between all the services and defined in /packages/api-base/src/auth.ts, many of them described as Lazy JavaScript Promises, based on where they are awaited in the respective resolver.
A couple examples of the mentioned authorization checkers is the expectUser
and expectAdmin
rule, which are defined as:
const expectUser = LazyPromise(async () => { const user = await userPromise;
assert(user, "Forbidden!");
return user;});
const expectAdmin = LazyPromise(async () => { const user = await expectUser;
assert(user.role === "ADMIN", "Forbidden");
return user;});
The expectUser
asserts that the authenticated user entity exists, and the expectAdmin
re-uses the expectUser
rule and asserts that the user also has the role ADMIN
.