Skip to content

Commit

Permalink
[WIP] withAuth example
Browse files Browse the repository at this point in the history
  • Loading branch information
timleslie committed May 12, 2021
1 parent deb7f95 commit 67e92e5
Show file tree
Hide file tree
Showing 14 changed files with 818 additions and 1 deletion.
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.
3 changes: 3 additions & 0 deletions docs/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export function Navigation() {
<NavItem href="/tutorials/getting-started-with-create-keystone-app">
Getting started
</NavItem>
<NavItem href="/tutorials/with-auth">
Authentication
</NavItem>
</Section>
<Section label="Guides">
<NavItem href="/guides/keystone-5-vs-keystone-next">Keystone 5 vs Next</NavItem>
Expand Down
112 changes: 112 additions & 0 deletions docs/pages/tutorials/with-auth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Markdown } from '../../components/Page';

Authentication

In this tutorial we're going to demonstrate how to add password based authentication to your Keystone system.

This tutorial builds on the [FIXME](./fixme) tutorials, so make sure you've completed that first before starting this one.
If you want to jump straight in. you can grab the starting code from [FIXME](./FIXME).

By the end of the tutorial you'll be able to add users with passwords to your task manager. and they'll be able to:

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


This project demonstrates how to add password based authentication to your Keystone system.
It builds on the [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 shows you how to add authentication to your 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 your system:

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

### Added fields

We start by adding two new fields, `email` and `password`, to the `Person` list.
These will be used as our _identity_ and _secret_ fields for login.

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

### Auth config

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

### Session

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

### Wrapped config

```typescript
export default withAuth(
config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./keystone-example.db',
},
lists,
session,
})
);
```

## Screenshots

### Admin UI

Screenshots of the first item experience, mailing list experience(?), login screen, the "logged in as" bit, logout button.
![initial user screen](../../screenshots/init-user-01.png)
![mailing list subscription screen](../../screenshots/mailing-list-01.png)
![sign in screen](../../screenshots/sign-in-screen-01.png)

### GraphQL Playground

Show off the new API that this has added and how to use it

### Somehow show session cookie data.

## Next steps

This project is a bare bones system, and doesn't use any of Keystone's advanced features.
We encourage you to experiment with the code here to see how Keystone works, become familiar with the Admin UI, and learn about the GraphQL Playground.

Once you've got the hang of using this project, you can check out the [feature examples](../).
These projects build on this starter project and show you how to use Keystones advanced features to take your project to the next level.

export default ({ children }) => <Markdown>{children}</Markdown>
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
93 changes: 93 additions & 0 deletions examples/with-auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
## Feature Example - Authentication

This project demonstrates how to add password based authentication to your Keystone system.
It builds on the [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 shows you how to add authentication to your 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 your system:

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

### Added fields

We start by adding two new fields, `email` and `password`, to the `Person` list.
These will be used as our _identity_ and _secret_ fields for login.

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

### Auth config

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

### Session

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

### Wrapped config

```typescript
export default withAuth(
config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./keystone-example.db',
},
lists,
session,
})
);
```

## Screenshots

### Admin UI

Screenshots of the first item experience, mailing list experience(?), login screen, the "logged in as" bit, logout button.
![initial user screen](../../screenshots/init-user-01.png)
![mailing list subscription screen](../../screenshots/mailing-list-01.png)
![sign in screen](../../screenshots/sign-in-screen-01.png)

### GraphQL Playground

Show off the new API that this has added and how to use it

### Somehow show session cookie data.

## Next steps

This project is a bare bones system, and doesn't use any of Keystone's advanced features.
We encourage you to experiment with the code here to see how Keystone works, become familiar with the Admin UI, and learn about the GraphQL Playground.

Once you've got the hang of using this project, you can check out the [feature examples](../).
These projects build on this starter project and show you how to use Keystones advanced features to take your project to the next level.
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

0 comments on commit 67e92e5

Please sign in to comment.