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

Wallet auth #55

Merged
merged 31 commits into from
Jun 29, 2023
Merged
Changes from 25 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c62db9f
WIP: server-side generated challenge
valiafetisov Jun 20, 2023
26a8e7c
fix max-len eslint rule
valiafetisov Jun 20, 2023
e7aec95
working resolvers (:
valiafetisov Jun 20, 2023
ae03db2
add Authentication section to the API readme
valiafetisov Jun 21, 2023
529f0a8
Challenge cleanup
valiafetisov Jun 21, 2023
fdbeed6
end-to-end api auth flow
valiafetisov Jun 21, 2023
7641457
update readme
valiafetisov Jun 21, 2023
d56b8f9
fix linter and add some logging
valiafetisov Jun 21, 2023
2bb6d66
frontend: replace signIn and signUp mutatons with createChallenge and…
valiafetisov Jun 26, 2023
f754939
frontend: sign in working with metamask
valiafetisov Jun 26, 2023
a42ec5e
frontend: add user address to the header
valiafetisov Jun 26, 2023
ea0a936
replace deprecated substr
valiafetisov Jun 26, 2023
23a4374
add icon to the button
valiafetisov Jun 26, 2023
5272ab9
always refetch me
valiafetisov Jun 26, 2023
598ce1b
WIP: half-fixed tests
valiafetisov Jun 27, 2023
002f13d
challange module 100% coverage
valiafetisov Jun 27, 2023
fc97e32
WIP: all tests are fixed, coverage fails
valiafetisov Jun 27, 2023
2c463d5
100% test coverage for models
valiafetisov Jun 27, 2023
a281433
refactor src/helpers into relevant module
valiafetisov Jun 27, 2023
ffc6ed1
fix types
valiafetisov Jun 27, 2023
865aad5
add more tests
valiafetisov Jun 27, 2023
3d3b48e
remove requirement of rpc url
valiafetisov Jun 27, 2023
5704fa6
update readme
valiafetisov Jun 27, 2023
88d2208
update readme + small improvements
valiafetisov Jun 27, 2023
e333af3
better user type
valiafetisov Jun 27, 2023
306e24c
typos
valiafetisov Jun 28, 2023
f0f6535
use matching ethers version on frontend
valiafetisov Jun 28, 2023
f48a24f
fixed types and typos
valiafetisov Jun 28, 2023
234bfe0
fix docker-compose
valiafetisov Jun 28, 2023
5d07ba0
frontend: throw correct error message if no extension is present
valiafetisov Jun 29, 2023
6393e72
update application screenshot
valiafetisov Jun 29, 2023
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
3 changes: 2 additions & 1 deletion api/.eslintrc
Original file line number Diff line number Diff line change
@@ -20,7 +20,8 @@
"import/prefer-default-export": "off",
"no-console": [
"error"
]
],
"max-len": ["error", { "code": 120 }]
}
}

95 changes: 91 additions & 4 deletions api/README.md
Original file line number Diff line number Diff line change
@@ -31,10 +31,11 @@ Some environment variables are pre-configured for the development. You can copy

- `DATABASE_URL` (required): path to the database file
- `JWT_SECRET` (required): server's jwt secret
- `PORT` (optional, default: 3000): port on which the server will run
- `AUTH_SIGNUP_ENABLED` (optional, default: `false`): if signing up mutation is allowed (i.e. user creation via endpoint is enabled)
- `JWT_EXPIRATION_PERIOD` (optional, default: `'7d'`): how soon the signed jwt token will expire
- `DEBUG` (optional): if set, enables the different more explicit logging mode where debug levels are set to `debug` for the app's logger and `query` for db logger
- `PORT` (optional, default `3000`): port on which the server will run
- `API_ORIGIN` (optional, default `http://0.0.0.0:${PORT}`): the URL at which the API is running. it's important to provide this variable in production since it influences the message signed during authorization
- `AUTH_SIGNUP_ENABLED` (optional, default: `false`): if signing up is allowed. In case it's not set, no new users can be created, but old users can still sign up
valiafetisov marked this conversation as resolved.
Show resolved Hide resolved
- `JWT_EXPIRATION_PERIOD` (optional, default: `'7d'`): how soon JWT token will expire
- `DEBUG` (optional, default not set): if set, enables more explicit logging mode where debug levels are set to `debug` for the app's logger and `query` for db logger

### Database

@@ -54,6 +55,92 @@ npx prisma studio

The configuration is received from the `logger.config.ts` file at the root of the project. Adjust the file parameters to control the logger behaviour.

## Authentication

The API authentication is implemented using "Sign-In with Ethereum" standard described in [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361). Basically, in order to get a usual JWT token, one have to sign a message provided by the API using their etherium wallet. In practice it means:

1. Create challenge via executing `createChallenge` graphql mutation
- Example request (that have to contain your public etherium address)
```gql
mutation {
createChallenge(
address: "paste_your_ethereum_address"
) {
nonce
message
hex
}
}
```

- Example response (that contains hex-encoded message)
```json
{
"data": {
"createChallenge": {
"nonce": "6f4c7f7cd61a499290e68a2740957407",
"message": "example.com wants you to sign in with your Ethereum account...",
"hex": "0x302e302e302e302077616e74732029..."
}
}
}
```
Where `hex` is just hex-encoded `message` that actually needs to be signed

2. Sign provided message
- Either [using your metamask wallet](https://docs.metamask.io/wallet/how-to/use-siwe/)
```js
// this should be executed at the browser console of the graphql playground
await ethereum.request({
method: 'personal_sign',
params: [
'paste_hex_from_the_above',
'paste_your_ethereum_address'
]
});
```

- Or using foundry command line tool called `cast` (note: you will be asked for your private key; for other auth methods, [read the cli docs](https://book.getfoundry.sh/reference/cast/cast-wallet-sign))
```sh
$ cast wallet sign -i "hex_from_the_above"
```

3. Provide signature back to the API to get usual JWT token back
- Example request
```gql
mutation {
solveChallenge(
nonce: "paste_nonce_from_step_1"
signature: "paste_signature_from_step_2"
) {
token
}
}
```

- Example response
```json
{
"data": {
"solveChallenge": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uSWQiOiI5ZGM1NjI3Mi1hMjBjLTRmM2YtYjM5MC1kZDc2NjE1NTA0YTYiLCJpYXQiOjE2ODczMzc2MDEsImV4cCI6MTY4Nzk0MjQwMX0.z1lJlKXnCbcex59JkU9j7hfRGhR2EBrnUE8phwPN7C0"
}
}
}
```

4. Use provided JWT token to make subsequent API requests
- Either sent as `Authorization: Bearer paste_token_from_step_3`
- Or set as `Authorization` cookie
- Example request
```gql
query {
me {
address
}
}
```

## Health endpoint

Endpoint available at `/healthz` path. Provides response if api is currently running and prisma (orm) is able to execute queries.
Loading