Skip to content

Commit

Permalink
Add support for optional scheme in authority (#195)
Browse files Browse the repository at this point in the history

---------

Co-authored-by: Simon Bihel <[email protected]>
  • Loading branch information
NicholasEllul and sbihel authored Apr 10, 2024
1 parent 0e63b05 commit 9ae4850
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 7 deletions.
16 changes: 15 additions & 1 deletion packages/siwe-parser/lib/abnf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { isEIP55Address, parseIntegerNumber } from "./utils";

const GRAMMAR = `
sign-in-with-ethereum =
domain %s" wants you to sign in with your Ethereum account:" LF
[ scheme "://" ] domain %s" wants you to sign in with your Ethereum account:" LF
address LF
LF
[ statement LF ]
Expand Down Expand Up @@ -161,6 +161,7 @@ class GrammarApi {
}

export class ParsedMessage {
scheme: string | null;
domain: string;
address: string;
statement: string | null;
Expand All @@ -179,6 +180,19 @@ export class ParsedMessage {
parser.ast = new apgLib.ast();
const id = apgLib.ids;

const scheme = function (state, chars, phraseIndex, phraseLength, data) {
const ret = id.SEM_OK;
if (state === id.SEM_PRE && phraseIndex === 0) {
data.scheme = apgLib.utils.charsToString(
chars,
phraseIndex,
phraseLength
);
}
return ret;
};
parser.ast.callbacks.scheme = scheme;

const domain = function (state, chars, phraseIndex, phraseLength, data) {
const ret = id.SEM_OK;
if (state === id.SEM_PRE) {
Expand Down
5 changes: 4 additions & 1 deletion packages/siwe-parser/lib/parsers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ describe("Successfully parses with ABNF Client", () => {
(test_name, test) => {
const parsedMessage = new ParsedMessage(test.message);
for (const [field, value] of Object.entries(test.fields)) {
if (typeof value === "object") {
if (value === null) {
expect(parsedMessage[field]).toBeUndefined();
}
else if (typeof value === "object") {
expect(parsedMessage[field]).toStrictEqual(value);
} else {
expect(parsedMessage[field]).toBe(value);
Expand Down
3 changes: 3 additions & 0 deletions packages/siwe/lib/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ describe(`Message verification without suppressExceptions`, () => {
.verify({
signature: test_fields.signature,
time: (test_fields as any).time || test_fields.issuedAt,
scheme: (test_fields as any).scheme,
domain: (test_fields as any).domainBinding,
nonce: (test_fields as any).matchNonce,
})
Expand Down Expand Up @@ -85,6 +86,7 @@ describe(`Message verification without suppressExceptions`, () => {
.verify({
signature: test_fields.signature,
time: (test_fields as any).time || test_fields.issuedAt,
scheme: (test_fields as any).scheme,
domain: (test_fields as any).domainBinding,
nonce: (test_fields as any).matchNonce,
})
Expand All @@ -109,6 +111,7 @@ describe(`Message verification with suppressExceptions`, () => {
{
signature: test_fields.signature,
time: (test_fields as any).time || test_fields.issuedAt,
scheme: (test_fields as any).scheme,
domain: (test_fields as any).domainBinding,
nonce: (test_fields as any).matchNonce,
},
Expand Down
23 changes: 20 additions & 3 deletions packages/siwe/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
} from './utils';

export class SiweMessage {
/**RFC 3986 URI scheme for the authority that is requesting the signing. */
scheme?: string;
/**RFC 4501 dns authority that is requesting the signing. */
domain: string;
/**Ethereum address performing the signing conformant to capitalization
Expand Down Expand Up @@ -69,6 +71,7 @@ export class SiweMessage {
constructor(param: string | Partial<SiweMessage>) {
if (typeof param === 'string') {
const parsedMessage = new ParsedMessage(param);
this.scheme = parsedMessage.scheme;
this.domain = parsedMessage.domain;
this.address = parsedMessage.address;
this.statement = parsedMessage.statement;
Expand All @@ -82,6 +85,7 @@ export class SiweMessage {
this.chainId = parsedMessage.chainId;
this.resources = parsedMessage.resources;
} else {
this.scheme = param?.scheme;
this.domain = param.domain;
this.address = param.address;
this.statement = param?.statement;
Expand Down Expand Up @@ -113,8 +117,8 @@ export class SiweMessage {
toMessage(): string {
/** Validates all fields of the object */
this.validateMessage();

const header = `${this.domain} wants you to sign in with your Ethereum account:`;
const headerPrefx = this.scheme ? `${this.scheme}://${this.domain}` : this.domain;
const header = `${headerPrefx} wants you to sign in with your Ethereum account:`;
const uriField = `URI: ${this.uri}`;
let prefix = [header, this.address].join('\n');
const versionField = `Version: ${this.version}`;
Expand Down Expand Up @@ -246,7 +250,20 @@ export class SiweMessage {
});
}

const { signature, domain, nonce, time } = params;
const { signature, scheme, domain, nonce, time } = params;

/** Scheme for domain binding */
if (scheme && scheme !== this.scheme) {
fail({
success: false,
data: this,
error: new SiweError(
SiweErrorType.SCHEME_MISMATCH,
scheme,
this.scheme
),
});
}

/** Domain binding */
if (domain && domain !== this.domain) {
Expand Down
11 changes: 9 additions & 2 deletions packages/siwe/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export interface VerifyParams {
/** Signature of the message signed by the wallet */
signature: string;

/** RFC 3986 URI scheme for the authority that is requesting the signing. */
scheme?: string;

/** RFC 4501 dns authority that is requesting the signing. */
domain?: string;

Expand All @@ -17,6 +20,7 @@ export interface VerifyParams {

export const VerifyParamsKeys: Array<keyof VerifyParams> = [
'signature',
'scheme',
'domain',
'nonce',
'time',
Expand Down Expand Up @@ -63,8 +67,8 @@ export class SiweError {
this.received = received;
}

/** Type of the error. */
type: SiweErrorType | string;
/** Type of the error. */
type: SiweErrorType | string;

/** Expected value or condition to pass. */
expected?: string;
Expand All @@ -83,6 +87,9 @@ export enum SiweErrorType {
/** `domain` is not a valid authority or is empty. */
INVALID_DOMAIN = 'Invalid domain.',

/** `scheme` don't match the scheme provided for verification. */
SCHEME_MISMATCH = 'Scheme does not match provided scheme for verification.',

/** `domain` don't match the domain provided for verification. */
DOMAIN_MISMATCH = 'Domain does not match provided domain for verification.',

Expand Down
28 changes: 28 additions & 0 deletions test/parsing_positive.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,5 +216,33 @@
"nonce": "15050747",
"issuedAt": "2022-06-30T14:08:51.382Z"
}
},
"domain contains optional scheme": {
"message": "https://example.com wants you to sign in with your Ethereum account:\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\nI accept the ExampleOrg Terms of Service: https://example.com/tos\n\nURI: https://example.com/login\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2021-09-30T16:25:24Z",
"fields": {
"scheme": "https",
"domain": "example.com",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"statement": "I accept the ExampleOrg Terms of Service: https://example.com/tos",
"uri": "https://example.com/login",
"version": "1",
"chainId": 1,
"nonce": "32891756",
"issuedAt": "2021-09-30T16:25:24Z"
}
},
"scheme is not parsed from elsehwere in message": {
"message": "localhost:3030 wants you to sign in with your Ethereum account:\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\n\nI accept the ExampleOrg Terms of Service: http://localhost:3030/tos\n\nURI: http://localhost:3030/login\nVersion: 1\nChain ID: 1\nNonce: 32891756\nIssued At: 2021-09-30T16:25:24Z",
"fields": {
"scheme": null,
"domain": "localhost:3030",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"statement": "I accept the ExampleOrg Terms of Service: http://localhost:3030/tos",
"uri": "http://localhost:3030/login",
"version": "1",
"chainId": 1,
"nonce": "32891756",
"issuedAt": "2021-09-30T16:25:24Z"
}
}
}

0 comments on commit 9ae4850

Please sign in to comment.