Skip to content
This repository was archived by the owner on Nov 14, 2025. It is now read-only.

Commit fb98d7e

Browse files
committed
refactor: switch from JWT to cookies for authentication (#36)
1 parent 1bc8314 commit fb98d7e

File tree

4 files changed

+74
-43
lines changed

4 files changed

+74
-43
lines changed

apps/api/src/classes/lnd.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import Buffer from 'node:buffer'
1+
import { Buffer } from 'node:buffer'
22
import fs from 'node:fs'
33
import net from 'node:net'
44
import process from 'node:process'
Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,18 @@
1-
const { LNMarketsRest } = require('@ln-markets/api')
2-
const { createLnurlAuthPubkeyAndSignature } = require('@/helpers/lnurl.js')
1+
import { LNMarketsRest } from '@ln-markets/api'
2+
import fetch from 'node-fetch'
33

4-
const isTokenExpired = (token) => {
5-
const convertBase64 = (input) => {
6-
input = input.replace(/-/g, '+').replace(/_/g, '/')
4+
import { createLnurlAuthPubkeyAndSignature } from '#src/helpers/lnurl.js'
75

8-
const pad = input.length % 4
6+
const isCookieExpired = (cookie) => {
7+
const expiry = Date.parse(
8+
cookie
9+
.split('; ')
10+
.find((property) => property.startsWith('Expires='))
11+
.substring(8) // Lenght of Expires=, to only get the date.
12+
)
13+
const now = Date.now()
914

10-
if (pad) {
11-
if (pad === 1) {
12-
throw new Error('InvalidLengthError')
13-
}
14-
input += new Array(5 - pad).join('=')
15-
}
16-
17-
return input
18-
}
19-
20-
const convertedString = convertBase64(token.split('.')[1])
21-
const payload = Buffer.from(convertedString, 'base64').toString()
22-
23-
const { exp } = JSON.parse(payload)
24-
const now = Math.floor(Date.now() / 1000)
25-
26-
return now >= exp
15+
return now > expiry
2716
}
2817

2918
const network = process.env.BITCOIN_NETWORK
@@ -34,20 +23,23 @@ const customHeaders = {
3423

3524
class LNMarketsAPI extends LNMarketsRest {
3625
constructor() {
37-
super({ network, customHeaders })
26+
super({ network, customHeaders, skipApiKey: true })
3827

39-
this.doNotCheckToken = false
28+
this.doNotCheckCookie = false
4029
}
4130

4231
async beforeRequestApi(options) {
4332
try {
44-
if (!this.doNotCheckToken) {
45-
if (!this.token || isTokenExpired(this.token)) {
46-
// Mutex du pauvre
47-
this.doNotCheckToken = true
48-
this.token = await this.createToken()
33+
if (
34+
!this.doNotCheckCookie &&
35+
(!this.cookie || isCookieExpired(this.cookie))
36+
) {
37+
await this.authenticate()
38+
}
4939

50-
this.doNotCheckToken = false
40+
if (options.credentials) {
41+
options.headers = {
42+
Cookie: this.cookie,
5143
}
5244
}
5345

@@ -57,16 +49,50 @@ class LNMarketsAPI extends LNMarketsRest {
5749
}
5850
}
5951

60-
async createToken() {
52+
async authenticate(opt = {}) {
6153
try {
62-
const { lnurl } = await this.getLnurlAuth()
63-
const params = await createLnurlAuthPubkeyAndSignature({ lnurl })
64-
const { token } = await this.lnurlAuth(params)
65-
return token
54+
const { withJWT = false } = opt
55+
56+
this.doNotCheckCookie = true
57+
58+
const authResponse = await fetch(
59+
'https://api.kibotrel.lnmarkets.dev/v1/lnurl/auth',
60+
{
61+
method: 'post',
62+
body: JSON.stringify({}),
63+
headers: { 'Content-Type': 'application/json' },
64+
credentials: true,
65+
}
66+
)
67+
68+
const cookie = authResponse.headers.get('set-cookie')
69+
const { lnurl } = await authResponse.json()
70+
71+
const params = new URLSearchParams(
72+
await createLnurlAuthPubkeyAndSignature({ lnurl, withJWT })
73+
)
74+
75+
const response = await fetch(
76+
`https://${this.hostname}/${
77+
this.version
78+
}/lnurl/auth?${params.toString()}`,
79+
{
80+
credentials: true,
81+
headers: { Cookie: cookie },
82+
}
83+
)
84+
85+
if (!withJWT) {
86+
this.cookie = cookie
87+
}
88+
89+
this.doNotCheckCookie = false
90+
91+
return response.json()
6692
} catch (error) {
6793
return Promise.reject(error)
6894
}
6995
}
7096
}
7197

72-
module.exports = new LNMarketsAPI()
98+
export default new LNMarketsAPI()

apps/api/src/helpers/lnurl.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ const arrayToHexString = (array) => {
2323
)
2424
}
2525

26-
export const createLnurlAuthPubkeyAndSignature = async ({ lnurl }) => {
26+
export const createLnurlAuthPubkeyAndSignature = async ({
27+
lnurl,
28+
withJWT = false,
29+
}) => {
2730
try {
2831
if (!lnurl) {
2932
throw new Error('lnurlNotFound')
@@ -90,7 +93,7 @@ export const createLnurlAuthPubkeyAndSignature = async ({ lnurl }) => {
9093
hmac,
9194
sig: hexStringSignature,
9295
key: publicKey,
93-
jwt: true,
96+
jwt: withJWT,
9497
}
9598
} catch (error) {
9699
return Promise.reject(error)

apps/api/src/routes/auth/get.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
const LNMarketsAPI = require('@/classes/lnmarkets-api.js')
1+
import LNMarketsAPI from '#src/classes/lnmarkets-api.js'
22

3-
module.exports = async (req, res, next) => {
3+
export default async (req, res, next) => {
44
try {
55
const parts = LNMarketsAPI.hostname.split('.')
6+
67
parts.shift()
7-
const token = await LNMarketsAPI.createToken()
8+
9+
const { token } = await LNMarketsAPI.authenticate({ withJWT: true })
810

911
res.json({ hostname: parts.join('.'), token })
1012
} catch (error) {

0 commit comments

Comments
 (0)