Skip to content

Commit

Permalink
Merge pull request #365 from energywebfoundation/SWTCH-2065/add_suppo…
Browse files Browse the repository at this point in the history
…rt_for_siwe

feat: add support for siwe
  • Loading branch information
nichonien authored Feb 22, 2023
2 parents ee6e9e9 + b5c9afe commit 99ea6e2
Show file tree
Hide file tree
Showing 14 changed files with 982 additions and 626 deletions.
50 changes: 27 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,9 @@ end

This class provides implementation for verification of issued roles credential. The verification ensures that the credentials were issued by the authorised issuers and are neither revoked nor expired. LoginStrategy can be configured to authenticate only EnergyWeb Roles.

LoginStrategy relies on [`IssuerVerification`](https://github.com/energywebfoundation/ew-credentials/blob/develop/packages/vc-verification/src/verifier/issuer-verification.ts) internally for verification of the roles credential.
To know more about `LoginStrategy` initialization check [How to configure LoginStrategy](./configure-loginstrategy.md).

In order to use LoginStrategy, one needs to intialise and provide :
[`RoleIssuerResolver`](./lib/RoleIssuerResolver.ts/)
[`RoleRevokerResolver`](./lib/RoleRevokerResolver.ts/)
[`RoleCredentialResolver`](./lib/RoleCredentialResolver.ts/)

Addresses for deployed contracts are exported by [`@energyweb/credential-governance`](https://github.com/energywebfoundation/ew-credentials/blob/develop/packages/credential-governance/src/chain-constants.ts). One can choose the addresses based on the chain they want to operate upon.

It is also possible to provide own implementation of these resolvers by implementing these [`Interfaces`](https://github.com/energywebfoundation/ew-credentials/tree/develop/packages/vc-verification/src/resolver). The purpose of these resolvers are to resolve authorities responsible for issuance and revocation of these role credentials.

To be able to use `LoginStrategy` to authorise DIDs based on role credentials, one can provide one of the two values - either flag `includeAllRoles` (_verifies all the role credential issued to given DID_) attribute to `true` or provide set of `acceptedRoles` (_DID needs to have atleast one of the metioned role credential issued to it_) while initialising `LoginStrategy`. `includeAllRoles` will override `acceptedRoles` in case both values are provided. Check other configurations / parameters for [`LoginStrategy`](./lib/LoginStrategy.ts/)
#### Example LoginStrategy Initialisation

```Typescript
import {
Expand Down Expand Up @@ -171,8 +162,12 @@ passport.use(
return _done(null, _payload);
})
);
```

const token = 'askjad...';
### Login with EIP191 Jwt token (Sign-in with Ethereum)

```typescript
const token = 'askjad...'; // EIP191 signed JWT
const payload = {
iss: `did:ethr:volta:0x1224....`,
claimData: {
Expand All @@ -183,21 +178,30 @@ const payload = {

await loginStrategy.validate(token, payload);
```
where the `iss` is DID of the subject and `blockNumber` is block number (or height) of the most recently mined block.

## Token payload structure
### Login with SIWE (Sign-in with Ethereum)

Token payload should have following structure
* While login with SIWE, one must initialise LoginStrategy with `siweMessageUri` (one of the attribute of `LoginStrategyOptions`).

```Typescript
{
claimData: {
blockNumber: number;
};
iss: string;
}
```
```typescript
const token = '0xdc35c7f8ba2720df052e0092556456127f00f7707eaa8e3bbff7e56774e7f2e05a093cfc9e02964c33d86e8e066e221b7d153d27e5a2e97ccd5ca7d3f2ce06cb1b'; // EIP712 signature

where the `iss` is DID of the subject and `blockNumber` is block number (or height) of the most recently mined block.
const sampleSiwePayload: Partial<SiweMessagePayload> = {
domain: 'login.xyz',
address: '0x9D85ca56217D2bb651b00f15e694EB7E713637D4',
statement: 'Sign-In With Ethereum Example Statement',
uri: 'https://login.xyz',
version: '1',
nonce: 'bTyXgcQxn2htgkjJn',
issuedAt: '2022-01-27T17:09:38.578Z',
chainId: 1,
expirationTime: '2100-01-07T14:31:43.952Z',
};

await loginStrategy.validate(token, payload);
```
> Read more about Sign-In with Ethereum [here](https://docs.login.xyz)
### Prerequisities

Expand Down
48 changes: 48 additions & 0 deletions configure-loginstrategy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## Configuring LoginStrategy

LoginStrategy constructor expects the following parameters for initialisation :
1. LoginStrategyOptions
2. IssuerResolver
3. RevokerResolver
4. CredentialResolver
5. verifyProof (method)

#### 1. [LoginStrategyOptions](https://github.com/energywebfoundation/passport-did-auth/blob/ee6e9e9d89ff5051c1cd59aed32186fccc72f9e6/lib/LoginStrategy.ts#L39)

LoginStrategy accepts number of attributes (optional and non-optional).

| Attributes | Allowed values | use case|
|------------|---------------|---------|
| claimField | string | field name which holds the claim in request |
| rpcUrl | string | rpc url for the blockchain, `ewc` or `volta` |
| cacheServerUrl | string | `ssi-hub` url |
| privateKey | string | PrivateKey of the user |
| ensResolvers | | Resolver contract address, used to resolve RoleDefinition (`RoleDefinitionREsolverV2` contract) |
| didContractAddress | string | DID Registry contract address (ERC1056) |
| ensRegistryAddress | string | ENS Contract address |
| ipfsUrl | string | IPFS Gateway |
| acceptedRoles | string[] | Roles needed to get authorised |
| includeAllRoles | boolean | If set to `true`, all holder's are required for authorisation |
| jwtSecret | string | Jwt secret required to encode response |
| jwtSignOptions | | |
| siweMessageUri | string | uri used in siwe message payload |


> Addresses for deployed contracts are exported by [`@energyweb/credential-governance`](https://github.com/energywebfoundation/ew-credentials/blob/develop/packages/credential-governance/src/chain-constants.ts). One can choose the addresses based on the chain they want to operate upon.
To be able to use `LoginStrategy` to authorise DIDs based on role credentials -
- one can provide one of the two values - either flag `includeAllRoles` (_verifies all the role credential issued to given DID_) attribute to `true`
- or provide set of `acceptedRoles` (_DID needs to have atleast one of the metioned role credential issued to it_) while initialising `LoginStrategy`. `includeAllRoles` will override `acceptedRoles` in case both values are provided.

### Resolvers

LoginStrategy relies on [`IssuerVerification`](https://github.com/energywebfoundation/ew-credentials/blob/develop/packages/vc-verification/src/verifier/issuer-verification.ts) internally for verification of the roles credential.

In order to use LoginStrategy, one needs to intialise and provide :

2. [`RoleIssuerResolver`](./lib/RoleIssuerResolver.ts/) - Resolves `issuer/s` for a `RoleDefinition`.
3. [`RoleRevokerResolver`](./lib/RoleRevokerResolver.ts/) - Resolves `revoker/s` for a `RoleDefinition`.
4. [`RoleCredentialResolver`](./lib/RoleCredentialResolver.ts/) - Resolves credentials of a holder.

It is also possible to provide own implementation of these resolvers by implementing these [`Interfaces`](https://github.com/energywebfoundation/ew-credentials/tree/develop/packages/vc-verification/src/resolver). The purpose of these resolvers are to resolve authorities responsible for issuance and revocation of these role credentials.

4 changes: 4 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
RoleCredentialStatus,
AuthorisedUser,
RoleStatus,
SiweReqPayload,
} from './lib/LoginStrategy.types';

export {
Expand All @@ -44,8 +45,11 @@ export {
RoleRevokerResolver,
RoleStatus,
ResolverContractType,
SiweReqPayload,
VOLTA_CHAIN_ID,
VOLTA_ERC_1056_ADDRESS,
VOLTA_ENS_REGISTRY_ADDRESS,
VOLTA_RESOLVER_V2_ADDRESS,
};

export * from './lib/errors';
29 changes: 25 additions & 4 deletions lib/BaseStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { Strategy } from 'passport';
import { Request } from 'express';
import { inherits } from 'util';
import { SiweReqPayload } from './LoginStrategy.types';
import { InvalidSiweMessage } from './errors';

export interface StrategyOptions {
name: string;
Expand Down Expand Up @@ -33,6 +35,14 @@ export abstract class BaseStrategy extends Strategy {
* @returns encoded token
*/
abstract extractToken(req: Request): string | null;
/**
* @abstract
* @description extracts siwe signature and message from request
*
* @param req object that encapsules request to protected endpoint
* @returns encoded siwe signature and message
*/
abstract extractSiwe(req: Request): SiweReqPayload | null;
/**
* @abstract
* @description decodes token payload
Expand All @@ -58,10 +68,14 @@ export abstract class BaseStrategy extends Strategy {
*/
authenticate(req: Request): void {
const token = this.extractToken(req);
if (!token) {
return this.fail('Missing credentials', 400);
let siweObject: SiweReqPayload | null;
try {
siweObject = this.extractSiwe(req);
} catch (e) {
throw new InvalidSiweMessage(
`Message ${req.body.message} can not be parsed to SiweMessage`
);
}
const tokenPayload = this.decodeToken(token);
const verified = (err, user, info) => {
if (err) {
return this.error(err);
Expand All @@ -71,7 +85,14 @@ export abstract class BaseStrategy extends Strategy {
}
this.success(user, info);
};
this.validate(token, tokenPayload, verified);
if (token) {
const tokenPayload = this.decodeToken(token);
this.validate(token, tokenPayload, verified);
} else if (siweObject) {
this.validate(siweObject.signature, siweObject.message, verified);
} else {
return this.fail('Missing credentials', 400);
}
}
}

Expand Down
Loading

0 comments on commit 99ea6e2

Please sign in to comment.