- Prerequisites
- Steps
- Initialise a New Graphweaver Project
- Install Dependencies
- Get Sample Database
- Import Database Schema
- Create Authentication Table
- Define the Authentication Entity
- Create Auth Connection
- Initialise Authentication Environment
- Starting Graphweaver
- Next Steps
In this guide, we'll walk through setting up magic link authentication for your Graphweaver project.
This guide will also use an Sqlite database as an example data provider. Feel free to follow along or replace with your own data source as needed.
Let’s get started.
Prerequisites
- Node.js and npm/pnpm: Make sure you have Node.js 20 or above along with npm and pnpm (8+) installed on your system.
- Some of the commands use
curl
andsqlite3
from the command line. These can be substituted with your own tools if you do not have them
Steps
Initialise a New Graphweaver Project
npx graphweaver@latest init
- You'll be prompted to name your project (e.g., example ).
- For this example choose the "Mikro-orm Sqlite backend". However, if you have a different datasource then use that.
- Confirm the creation of the new Graphweaver app.
Install Dependencies
cd example
pnpm add @exogee/graphweaver-auth @exogee/graphweaver-auth-ui-components @exogee/graphweaver-admin-ui-components
This installs:
@exogee/graphweaver-auth
: Core Graphweaver authentication package used in the backend.@exogee/graphweaver-auth-ui-components
: Pre-built UI components for login/logout these are used by the AdminUI.@exogee/graphweaver-admin-ui-components
: The components used by the AdminUI.
Get Sample Database
Next, lets download the sample database:
curl -L -o database.sqlite https://github.com/lerocha/chinook-database/raw/master/ChinookDatabase/DataSources/Chinook_Sqlite.sqlite
- This downloads a sample SQLite database named 'Chinook_Sqlite.sqlite' and renames it to 'database.sqlite'. We'll use this to store user credentials.
Import Database Schema
Next, we can introspect the database schema which will create the Graphweaver source files.
pnpm run import sqlite
What is the database name? database.sqlite
Overwrite this file backend/schema/index.ts? yes
Overwrite this file backend/database.ts? yes
- This command analyzes the SQLite database and generates TypeScript code representing its schema.
- It overwrites existing
backend/schema/index.ts
andbackend/database.ts
files to integrate the new database structure into your Graphweaver project.
Create Authentication Table
Next, let’s add a new table to the database to store our magic links:
sqlite3 ./database.sqlite "CREATE TABLE Authentication (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type VARCHAR(255) NOT NULL,
user_id INTEGER NOT NULL,
data TEXT NOT NULL,
created_at DATE DEFAULT (datetime('now','localtime'))
);"
- This directly interacts with the SQLite database to create a table named 'Authentication'
- This table will store magic link information
Define the Authentication Entity
Now that we have added the table in the database we need to tell Graphweaver about it. To do that open the project in your editor and navigate to the ./src/backend/entities/sqlite
directory.
Then create a new file ./src/backend/entities/sqlite/authentication.ts
with this contents:
touch ./src/backend/entities/sqlite/authentication.ts
import {
Entity,
PrimaryKey,
Property,
JsonType,
BigIntType,
} from "@mikro-orm/core";
import type { AuthenticationBaseEntity } from "@exogee/graphweaver-auth";
@Entity({ tableName: "Authentication" })
export class Authentication<T> implements AuthenticationBaseEntity<T> {
@PrimaryKey({ type: new BigIntType("string") })
id!: string;
@Property({ type: String })
type!: string;
@Property({ type: new BigIntType("string") })
userId!: string;
@Property({ type: JsonType })
data!: T;
@Property({ type: Date })
createdAt!: Date;
}
We also need to make sure that the index file (./src/backend/entities/sqlite/index.ts
) is updated to export this file:
import { Album } from "./album";
import { Artist } from "./artist";
import { Authentication } from "./authentication";
import { Customer } from "./customer";
import { Employee } from "./employee";
import { Genre } from "./genre";
import { Invoice } from "./invoice";
import { InvoiceLine } from "./invoice-line";
import { MediaType } from "./media-type";
import { Playlist } from "./playlist";
import { Track } from "./track";
export * from "./album";
export * from "./artist";
export * from "./authentication";
export * from "./customer";
export * from "./employee";
export * from "./genre";
export * from "./invoice";
export * from "./invoice-line";
export * from "./media-type";
export * from "./playlist";
export * from "./track";
export const entities = [
Album,
Artist,
Authentication,
Customer,
Employee,
Genre,
Invoice,
InvoiceLine,
MediaType,
Playlist,
Track,
];
Create Auth Connection
In order to enable magic link authentication in the app we need to add the following to our ./src/backend/index.ts
:
/* example Graphweaver Project */
import Graphweaver from '@exogee/graphweaver-server';
import './schema';
import {
MagicLink,
MagicLinkData,
setAddUserToContext,
UserProfile,
} from '@exogee/graphweaver-auth';
import { MikroBackendProvider } from '@exogee/graphweaver-mikroorm';
import { Authentication } from './entities';
import { connection } from './database';
export const addUserToContext = async () => {
return new UserProfile({
id: '1',
email: 'test@test.com',
roles: ['LoggedInUser'],
});
};
setAddUserToContext(addUserToContext);
export const magicLink = new MagicLink({
provider: new MikroBackendProvider(Authentication<MagicLinkData>, connection),
getUser: async (): Promise<UserProfile<string>> => {
return addUserToContext();
},
sendMagicLink: async (url: URL): Promise<boolean> => {
// In a production system this would email / sms the magic link and you would not log to the console!
console.log(`\n\n ######## MagicLink: ${url.toString()} ######## \n\n`);
return true;
},
});
export const graphweaver = new Graphweaver();
export const handler = graphweaver.handler();
This code sets up the core magic link integration in your backend:
- It imports necessary modules from
@exogee/graphweaver-auth
. - It creates an
MagicLink
instance to handle login interactions. - The
addUserToContext
function defines how to map the logged in userId to a basic user profile when a user logs in. You can customise this to fetch additional user data from any data store. setAddUserToContext
configures Graphweaver to use youraddUserToContext
function to retrieve user information and add it to the GraphQL context.
Initialise Authentication Environment
There are a few extra files needed when setting up authentication. This includes an environment variable file and a graphweaver-config.js
file.
These are a bit tricky to setup at first but luckily for us there is a command line tool that can help.
To run it:
pnpm graphweaver init-auth magic-link
Environment file generated (./.env)
Config file generated (./graphweaver-config.js)
magic-link auth initialised successfully
- This command sets up the authentication system within Graphweaver:
- Generates an
.env
file with new keys used to sign JWT tokens - Configures Graphweaver and creates a new
graphweaver-config.js
file.
Starting Graphweaver
You've successfully set up password authentication in your Graphweaver project!
Let’s start the server and login:
pnpm start
Navigate to http://localhost:9000 and you will be asked to enter an email. This can be any email address at this stage as the magic link will be in the console output of the server.
You can update sendMagicLink
we created above to integrate your email server.
The login link will looks something like this:
######## MagicLink: http://localhost:9000/auth/login?redirect_uri=http%3A%2F%2Flocalhost%3A9000%2F&providers=mgl&token=7e112b2e-0d15-4efd-87fe-43a220be54a2&username=test%40test.com ########
Click on that link and you will be logged in to Graphweaver.
Next Steps
- Email Provider: Setup your email provider to connect the
sendMagicLink
function to your email provider. - Connect to User Table: There is no user table in this example to store usernames or email address. Update the
getUser
function to connect to your user data source. - Customise User Profiles: The
addUserToContext
function is a basic example. You'll likely want to fetch additional user information (e.g., email, name) and store it in your database. - Permissions and Authorisation: There are currently no ACLs. You'll need to implement proper authorisation rules to control what different users can do in your app.