Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/siwe-resources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ox": patch
---

Fixed `Siwe.parseMessage` losing or truncating resources when `Resources:` appeared in another message field or resource.
34 changes: 21 additions & 13 deletions src/core/Siwe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const prefixRegex =

// https://regexr.com/80gf9
export const suffixRegex =
/(?:URI: (?<uri>.+))\n(?:Version: (?<version>.+))\n(?:Chain ID: (?<chainId>\d+))\n(?:Nonce: (?<nonce>[a-zA-Z0-9]+))\n(?:Issued At: (?<issuedAt>.+))(?:\nExpiration Time: (?<expirationTime>.+))?(?:\nNot Before: (?<notBefore>.+))?(?:\nRequest ID: (?<requestId>.+))?/
/(?:URI: (?<uri>.+))\n(?:Version: (?<version>.+))\n(?:Chain ID: (?<chainId>\d+))\n(?:Nonce: (?<nonce>[a-zA-Z0-9]+))\n(?:Issued At: (?<issuedAt>.+))(?:\nExpiration Time: (?<expirationTime>.+))?(?:\nNot Before: (?<notBefore>.+))?(?:\nRequest ID: (?<requestId>.*))?(?:\nResources:(?<resources>(?:\n- .+)*))?/

/** [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) message fields. */
export type Message = {
Expand Down Expand Up @@ -372,18 +372,26 @@ export function parseMessage(message: string): ExactPartial<Message> {
scheme?: string
statement?: string
}
const { chainId, expirationTime, issuedAt, notBefore, requestId, ...suffix } =
(message.match(suffixRegex)?.groups ?? {}) as {
chainId: string
expirationTime?: string
issuedAt?: string
nonce: string
notBefore?: string
requestId?: string
uri: string
version: '1'
}
const resources = message.split('Resources:')[1]?.split('\n- ').slice(1)
const {
chainId,
expirationTime,
issuedAt,
notBefore,
requestId,
resources: resources_,
...suffix
} = (message.match(suffixRegex)?.groups ?? {}) as {
chainId: string
expirationTime?: string
issuedAt?: string
nonce: string
notBefore?: string
requestId?: string
resources?: string
uri: string
version: '1'
}
const resources = resources_?.split('\n- ').slice(1)
return {
...prefix,
...suffix,
Expand Down
147 changes: 147 additions & 0 deletions src/core/_test/Siwe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,153 @@ Resources:
`)
})

test('behavior: "Resources:" in statement', () => {
const message = `example.com wants you to sign in with your Ethereum account:
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e

See the Resources: list below.

URI: https://example.com/path
Version: 1
Chain ID: 1
Nonce: foobarbaz
Issued At: 2023-02-01T00:00:00.000Z
Resources:
- https://example.com/foo
- https://example.com/bar`
const parsed = Siwe.parseMessage(message)
expect(parsed).toMatchInlineSnapshot(`
{
"address": "0xA0Cf798816D4b9b9866b5330EEa46a18382f251e",
"chainId": 1,
"domain": "example.com",
"issuedAt": 2023-02-01T00:00:00.000Z,
"nonce": "foobarbaz",
"resources": [
"https://example.com/foo",
"https://example.com/bar",
],
"statement": "See the Resources: list below.",
"uri": "https://example.com/path",
"version": "1",
}
`)
})

test('behavior: "Resources:" in statement without resources', () => {
const message = `example.com wants you to sign in with your Ethereum account:
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e

See the Resources: page.

URI: https://example.com/path
Version: 1
Chain ID: 1
Nonce: foobarbaz
Issued At: 2023-02-01T00:00:00.000Z`
const parsed = Siwe.parseMessage(message)
expect(parsed.resources).toBeUndefined()
})

test('behavior: "Resources:" in uri', () => {
const message = `example.com wants you to sign in with your Ethereum account:
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e


URI: https://example.com/Resources:path
Version: 1
Chain ID: 1
Nonce: foobarbaz
Issued At: 2023-02-01T00:00:00.000Z
Resources:
- https://example.com/foo`
const parsed = Siwe.parseMessage(message)
expect(parsed.resources).toMatchInlineSnapshot(`
[
"https://example.com/foo",
]
`)
})

test('behavior: "Resources:" in requestId', () => {
const message = `example.com wants you to sign in with your Ethereum account:
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e


URI: https://example.com/path
Version: 1
Chain ID: 1
Nonce: foobarbaz
Issued At: 2023-02-01T00:00:00.000Z
Request ID: Resources:123
Resources:
- https://example.com/foo`
const parsed = Siwe.parseMessage(message)
expect(parsed.resources).toMatchInlineSnapshot(`
[
"https://example.com/foo",
]
`)
})

test('behavior: "Resources:" in a resource', () => {
const message = `example.com wants you to sign in with your Ethereum account:
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e


URI: https://example.com/path
Version: 1
Chain ID: 1
Nonce: foobarbaz
Issued At: 2023-02-01T00:00:00.000Z
Resources:
- https://example.com/Resources:foo
- https://example.com/bar`
const parsed = Siwe.parseMessage(message)
expect(parsed.resources).toMatchInlineSnapshot(`
[
"https://example.com/Resources:foo",
"https://example.com/bar",
]
`)
})

test('behavior: empty requestId with resources', () => {
// EIP-4361 allows an empty Request ID (`request-id = *pchar`).
const message = `example.com wants you to sign in with your Ethereum account:
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e


URI: https://example.com/path
Version: 1
Chain ID: 1
Nonce: foobarbaz
Issued At: 2023-02-01T00:00:00.000Z
Request ID: \nResources:
- https://example.com/foo`
const parsed = Siwe.parseMessage(message)
expect(parsed.resources).toMatchInlineSnapshot(`
[
"https://example.com/foo",
]
`)
})

test('behavior: empty resources', () => {
const message = Siwe.createMessage({
address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
chainId: 1,
domain: 'example.com',
issuedAt: new Date('2023-02-01T00:00:00.000Z'),
nonce: 'foobarbaz',
resources: [],
statement: 'Resources:',
uri: 'https://example.com/path',
version: '1',
})
expect(Siwe.parseMessage(message).resources).toMatchInlineSnapshot('[]')
})

test('behavior: no suffix', () => {
const message = `https://example.com wants you to sign in with your Ethereum account:
0xA0Cf798816D4b9b9866b5330EEa46a18382f251e
Expand Down
Loading