-
-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: magic-link; password-free login (#1150)
Co-authored-by: Léo Pradel <pradel.leo@gmail.com>
- Loading branch information
Showing
74 changed files
with
1,828 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
--- | ||
'@accounts/client': minor | ||
'@accounts/client-magic-link': minor | ||
'@accounts/database-manager': minor | ||
'@accounts/mongo': minor | ||
'@accounts/mongo-magic-link': minor | ||
'@accounts/typeorm': minor | ||
'@accounts/graphql-api': minor | ||
'@accounts/graphql-client': minor | ||
'@accounts/magic-link': minor | ||
'@accounts/rest-client': minor | ||
'@accounts/rest-express': minor | ||
'@accounts/server': minor | ||
'@accounts/types': minor | ||
--- | ||
|
||
Add support for magic-link strategy 🎉. | ||
|
||
Installation: | ||
|
||
```sh | ||
yarn add @accounts/magic-link | ||
``` | ||
|
||
Usage: | ||
|
||
```js | ||
import AccountsMagicLink from '@accounts/magic-link'; | ||
|
||
const accountsMagicLink = new AccountsMagicLink({}); | ||
|
||
const accountsServer = new AccountsServer( | ||
{ db: accountsDb, tokenSecret: 'secret' }, | ||
{ | ||
magicLink: accountsMagicLink, | ||
} | ||
); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# magic-link-server-typescript | ||
|
||
This example demonstrate how to use setup [accounts-js](https://github.com/accounts-js/accounts)' magic link module on the server. | ||
|
||
## Setup example | ||
|
||
In order to be able to run this example on your machine you first need to do the following steps: | ||
|
||
- Clone the repository `git clone git@github.com:accounts-js/accounts.git` | ||
- Install project dependencies: `yarn` | ||
- Link together all the packages: `yarn setup` | ||
- Compile the packages `yarn compile` | ||
- Go to the example folder `cd examples/magic-link-server-typescript` | ||
|
||
## Prerequisites | ||
|
||
You will need a MongoDB server to run this server. If you don't have a MongoDB server running already, and you have Docker & Docker Compose, you can do | ||
|
||
```bash | ||
docker-compose up -d | ||
``` | ||
|
||
to start a new one. | ||
|
||
## Getting Started | ||
|
||
This example sets up the module, then proceeds to run a simple test, before exiting. See the graphql-server-typescript example | ||
for a more comprehensive example of general accounts use. | ||
|
||
To run the example: | ||
|
||
```bash | ||
yarn start | ||
``` | ||
|
||
You should see output on the console suggesting that authenticating using a token was | ||
a success. There are quite a few comments in the code saying something about best practice | ||
if using the lower level functionality. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
version: '3.6' | ||
services: | ||
db-mongo-accounts: | ||
image: mongo:3.6.5-jessie | ||
ports: | ||
- '27017:27017' | ||
restart: always |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "@examples/magic-link-server-typescript", | ||
"private": true, | ||
"version": "0.32.0", | ||
"main": "lib/index.js", | ||
"license": "MIT", | ||
"scripts": { | ||
"start": "NODE_ENV=development nodemon -w src -x ts-node src/index.ts", | ||
"build": "tsc", | ||
"test": "yarn build" | ||
}, | ||
"dependencies": { | ||
"@accounts/database-manager": "^0.32.0", | ||
"@accounts/mongo": "^0.32.0", | ||
"@accounts/server": "^0.32.0", | ||
"@accounts/magic-link": "^0.1.0", | ||
"mongoose": "5.9.25", | ||
"tslib": "2.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/mongoose": "5.7.32", | ||
"@types/node": "14.0.13", | ||
"nodemon": "2.0.4", | ||
"ts-node": "8.10.2", | ||
"typescript": "3.9.7" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import mongoose from 'mongoose'; | ||
import MongoDBInterface from '@accounts/mongo'; | ||
import { DatabaseManager } from '@accounts/database-manager'; | ||
import AccountsMagicLink from '@accounts/magic-link'; | ||
import { AccountsServer } from '@accounts/server/lib/accounts-server'; | ||
|
||
const start = async () => { | ||
await mongoose.connect('mongodb://localhost:27017/accounts-js-magic-link-example', { | ||
useNewUrlParser: true, | ||
}); | ||
const mongoConn = mongoose.connection; | ||
|
||
// Build a storage for storing users | ||
const userStorage = new MongoDBInterface(mongoConn); | ||
// Create database manager (create user, find users, sessions etc) for accounts-js | ||
const accountsDb = new DatabaseManager({ | ||
sessionStorage: userStorage, | ||
userStorage, | ||
}); | ||
|
||
const accountsMagicLink = new AccountsMagicLink({}); | ||
|
||
// Create accounts server that holds a lower level of all accounts operations | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const accountsServer = new AccountsServer( | ||
{ db: accountsDb, tokenSecret: 'secret' }, | ||
{ | ||
magicLink: accountsMagicLink, | ||
} | ||
); | ||
|
||
// Setup a user (or use one from a previous run) | ||
const user = await userStorage.findUserByUsername('magnus'); | ||
|
||
const userId = user | ||
? user.id | ||
: await userStorage.createUser({ | ||
email: 'magnus@accounts.js.org', | ||
username: 'magnus', | ||
password: 'secret!123', | ||
}); | ||
|
||
// We clear previous tokens since an error with such a low quality token as used | ||
// below can cause issues moving on. | ||
await userStorage.removeAllLoginTokens(userId); | ||
|
||
// Before a user can use a token to authenticate or login, the token must be | ||
// registered on the user. You can use whichever token generator you want, but | ||
// generateRandomToken available in the server package can be used (and is used) | ||
// internally. The token must be unique as it internally is used to find the user | ||
// during login. | ||
await userStorage.addLoginToken(userId, 'magnus@accounts.js.org', 'goodtoken'); | ||
|
||
// Here we show that you can authenticate using the token, the function that | ||
// will be used if logging in through the general accounts interface. | ||
const authenticated = await accountsMagicLink.authenticate({ token: 'goodtoken' }); | ||
|
||
// See in the output that we successfully authenticated the user. If the authentication | ||
// failed, the example will exit with a stacktrace. | ||
if (authenticated) { | ||
console.log('Success! User logged in using token from magic link'); | ||
} | ||
}; | ||
|
||
start(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"compilerOptions": { | ||
"outDir": "./lib", | ||
"target": "es5", | ||
"lib": ["es2015", "esnext.asynciterable"], | ||
"sourceMap": true, | ||
"importHelpers": true, | ||
"esModuleInterop": true, | ||
"skipLibCheck": true | ||
}, | ||
"include": ["./src/**/*"], | ||
"exclude": ["node_modules", "lib"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# @accounts/client-magic-link | ||
|
||
[![npm](https://img.shields.io/npm/v/@accounts/client-magic-link)](https://www.npmjs.com/package/@accounts/client-magic-link) | ||
[![npm downloads](https://img.shields.io/npm/dm/@accounts/client-magic-link)](https://www.npmjs.com/package/@accounts/client-magic-link) | ||
[![codecov](https://img.shields.io/codecov/c/github/accounts-js/accounts)](https://codecov.io/gh/accounts-js/accounts) | ||
[![License](https://img.shields.io/github/license/accounts-js/accounts)](https://github.com/accounts-js/accounts/blob/master/LICENSE) | ||
|
||
## Documentation | ||
|
||
- [Website documentation](https://www.accountsjs.com/docs/strategies/magic-link-client) | ||
- [API documentation](https://www.accountsjs.com/docs/api/client-magic-link/globals) | ||
|
||
## Installation | ||
|
||
``` | ||
yarn add @accounts/client-magic-link | ||
``` | ||
|
||
## Contributing | ||
|
||
Any contribution is very welcome, read our [contributing guide](https://github.com/accounts-js/accounts/blob/master/CONTRIBUTING.md) to see how to locally setup the repository and see our development process. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { AccountsClientMagicLink } from '../src/client-magic-link'; | ||
|
||
const mockedClient = { | ||
transport: { | ||
requestMagicLinkEmail: jest.fn(), | ||
}, | ||
loginWithService: jest.fn(), | ||
}; | ||
const accountsMagicLink = new AccountsClientMagicLink(mockedClient as any); | ||
|
||
const user = { | ||
email: 'john@doe.com', | ||
token: 'deadbeef', | ||
}; | ||
|
||
describe('AccountsClientMagicLink', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('requires the client', async () => { | ||
expect(() => new AccountsClientMagicLink(null as any)).toThrowError( | ||
'A valid client instance is required' | ||
); | ||
}); | ||
|
||
describe('login', () => { | ||
it('should call client', async () => { | ||
await accountsMagicLink.login(user as any); | ||
expect(mockedClient.loginWithService).toHaveBeenCalledTimes(1); | ||
expect(mockedClient.loginWithService).toHaveBeenCalledWith('magic-link', { | ||
email: user.email, | ||
token: user.token, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('requestMagicLinkEmail', () => { | ||
it('should call transport', async () => { | ||
await accountsMagicLink.requestMagicLinkEmail(user.email); | ||
expect(mockedClient.transport.requestMagicLinkEmail).toHaveBeenCalledWith(user.email); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{ | ||
"name": "@accounts/client-magic-link", | ||
"version": "0.1.0", | ||
"description": "@accounts/client-magic-link", | ||
"main": "lib/index.js", | ||
"typings": "lib/index.d.ts", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"scripts": { | ||
"clean": "rimraf lib", | ||
"start": "tsc --watch", | ||
"precompile": "yarn clean", | ||
"compile": "tsc", | ||
"prepublishOnly": "yarn compile", | ||
"test": "yarn testonly", | ||
"test-ci": "yarn lint && yarn coverage", | ||
"testonly": "jest", | ||
"test:watch": "jest --watch", | ||
"coverage": "yarn testonly --coverage" | ||
}, | ||
"files": [ | ||
"src", | ||
"lib" | ||
], | ||
"jest": { | ||
"preset": "ts-jest" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/accounts-js/accounts/tree/master/packages/client-magic-link" | ||
}, | ||
"author": "Leo Pradel", | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@types/jest": "25.2.3", | ||
"@types/node": "14.0.14", | ||
"jest": "26.6.3", | ||
"rimraf": "3.0.2", | ||
"ts-jest": "26.5.0" | ||
}, | ||
"dependencies": { | ||
"@accounts/client": "^0.32.0", | ||
"@accounts/types": "^0.32.0", | ||
"tslib": "2.1.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { AccountsClient } from '@accounts/client'; | ||
import { LoginResult, LoginUserMagicLinkService } from '@accounts/types'; | ||
|
||
export class AccountsClientMagicLink { | ||
private client: AccountsClient; | ||
|
||
constructor(client: AccountsClient) { | ||
if (!client) { | ||
throw new Error('A valid client instance is required'); | ||
} | ||
this.client = client; | ||
} | ||
|
||
/** | ||
* Log the user in with a token. | ||
*/ | ||
public async login(user: LoginUserMagicLinkService): Promise<LoginResult> { | ||
return this.client.loginWithService('magic-link', user); | ||
} | ||
|
||
/** | ||
* Request a new login link. | ||
* @param {string} email - The email address to send a login link. | ||
*/ | ||
public requestMagicLinkEmail(email: string): Promise<void> { | ||
return this.client.transport.requestMagicLinkEmail(email); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { AccountsClientMagicLink } from './client-magic-link'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"extends": "../../tsconfig", | ||
"compilerOptions": { | ||
"rootDir": "./src", | ||
"outDir": "./lib", | ||
"importHelpers": true, | ||
"target": "es5" | ||
}, | ||
"exclude": ["node_modules", "__tests__", "lib"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.