Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

withAuth example #5612

Merged
merged 1 commit into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quick-stingrays-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-next/example-next-auth': major
---

Initial version of the `withAuth` example.
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ You can use these as a starting point for general experimentation in a clean env
Each of the examples below demonstrates a particular feature of Keystone.
You can use these projects to learn about, and experiment with specific features.

(coming soon)
* [`withAuth`](./with-auth): Adding password based authentication to your Keystone application.

## Solution Examples

Expand Down
1 change: 1 addition & 0 deletions examples/with-auth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @keystone-next/example-with-auth
82 changes: 82 additions & 0 deletions examples/with-auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
## Feature Example - Authentication

This project demonstrates how to add password based authentication to your Keystone system.
It builds on the [Task Manager](../todo) starter project.

## Instructions

To run this project, clone the Keystone repository locally then navigate to this directory and run:

```shell
yarn dev
```

This will start the Admin UI at [localhost:3000](http://localhost:3000).
You can use the Admin UI to create items in your database.

You can also access a GraphQL Playground at [localhost:3000/api/graphql](http://localhost:3000/api/graphql), which allows you to directly run GraphQL queries and mutations.

## Features

This project demonstrates how to add authentication to a Keystone system.
We're going to use the [`@keystone-next/auth`](https://next.keystonejs.com/apis/auth) package, along with Keystone's [session management API](https://next.keystonejs.com/apis/session), to add the following features to the base system:

* Configure which fields to use for signin
* Set up stateless session handling to keep track of the signed in user
* Add a signin screen to the Admin UI
* Add a signout button to the Admin UI
* Allow the signed in user to access their own details in the Admin UI
* Add a helper page to the Admin UI to allow you to create your first user when starting from an empty database

### Added fields

In this example we add two new fields, `email` and `password`, to the `Person` list.
These are used as our _identity_ and _secret_ fields for login.

```typescript
email: text({ isRequired: true, isUnique: true }),
password: password({ isRequired: true }),
```

### Auth config

We use the `createAuth` function from `@keystone-next/auth` to configure a `withAuth` config wrapper, which will inject all the extra config used to enabled configuration.

```typescript
import { createAuth } from '@keystone-next/auth';

const { withAuth } = createAuth({
listKey: 'Person',
identityField: 'email',
secretField: 'password',
initFirstItem: { fields: ['name', 'email', 'password'] },
});
```

### Session

We use a basic `statelessSessions` from `@keystone-next/keystone/session` for session handling.
You need to have sessions enabled in order to use the `withAuth` config wrapper.

```typescript
import { statelessSessions } from '@keystone-next/keystone/session';

const session = statelessSessions({ secret: '-- EXAMPLE COOKIE SECRET; CHANGE ME --' });
```

### Wrapped config

We wrap our config using the `withAuth` function, which injects added Admin UI configuration, as well as the GraphQL queries and mutations for authentication.

```typescript
export default withAuth(
config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./keystone-example.db',
},
lists,
session,
})
);
```
45 changes: 45 additions & 0 deletions examples/with-auth/keystone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { config } from '@keystone-next/keystone/schema';
import { statelessSessions } from '@keystone-next/keystone/session';
import { createAuth } from '@keystone-next/auth';
import { lists } from './schema';

// createAuth configures signin functionality based on the config below. Note this only implements
// authentication, i.e signing in as an item using identity and secret fields in a list. Session
// management and access control are controlled independently in the main keystone config.
const { withAuth } = createAuth({
// This is the list that contains items people can sign in as
listKey: 'Person',
// The identity field is typically a username or email address
identityField: 'email',
// The secret field must be a password type field
secretField: 'password',

// initFirstItem turns on the "First User" experience, which prompts you to create a new user
// when there are no items in the list yet
initFirstItem: {
// These fields are collected in the "Create First User" form
fields: ['name', 'email', 'password'],
},
});

// Stateless sessions will store the listKey and itemId of the signed-in user in a cookie.
// This session object will be made availble on the context object used in hooks, access-control,
// resolvers, etc.
const session = statelessSessions({
// The session secret is used to encrypt cookie data (should be an environment variable)
secret: '-- EXAMPLE COOKIE SECRET; CHANGE ME --',
});

// We wrap our config using the withAuth function. This will inject all
// the extra config required to add support for authentication in our system.
export default withAuth(
config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./keystone-example.db',
},
lists,
// We add our session configuration to the system here.
session,
})
);
23 changes: 23 additions & 0 deletions examples/with-auth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@keystone-next/example-with-auth",
"version": "0.0.0",
"private": true,
"license": "MIT",
"scripts": {
"dev": "keystone-next dev",
"start": "keystone-next start",
"build": "keystone-next build"
},
"dependencies": {
"@keystone-next/auth": "^23.0.0",
"@keystone-next/fields": "^8.0.0",
"@keystone-next/keystone": "^17.0.0"
},
"devDependencies": {
"typescript": "^4.2.4"
},
"engines": {
"node": ">=v12.13.1"
},
"repository": "https://github.com/keystonejs/keystone/tree/master/examples/with-auth"
}
Loading