• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / auth-js / 17292897055

28 Aug 2025 10:18AM UTC coverage: 80.811%. Remained the same
17292897055

Pull #1105

github

web-flow
Merge a6a43573f into be9a27cc7
Pull Request #1105: chore: secure-proof workflows

1080 of 1441 branches covered (74.95%)

Branch coverage included in aggregate %.

1451 of 1691 relevant lines covered (85.81%)

173.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/src/lib/base64url.ts
1
/**
2
 * Avoid modifying this file. It's part of
3
 * https://github.com/supabase-community/base64url-js.  Submit all fixes on
4
 * that repo!
5
 */
6

7
/**
8
 * An array of characters that encode 6 bits into a Base64-URL alphabet
9
 * character.
10
 */
11
const TO_BASE64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'.split('')
16✔
12

13
/**
14
 * An array of characters that can appear in a Base64-URL encoded string but
15
 * should be ignored.
16
 */
17
const IGNORE_BASE64URL = ' \t\n\r='.split('')
16✔
18

19
/**
20
 * An array of 128 numbers that map a Base64-URL character to 6 bits, or if -2
21
 * used to skip the character, or if -1 used to error out.
22
 */
23
const FROM_BASE64URL = (() => {
16✔
24
  const charMap: number[] = new Array(128)
16✔
25

26
  for (let i = 0; i < charMap.length; i += 1) {
16✔
27
    charMap[i] = -1
2,048✔
28
  }
29

30
  for (let i = 0; i < IGNORE_BASE64URL.length; i += 1) {
16✔
31
    charMap[IGNORE_BASE64URL[i].charCodeAt(0)] = -2
80✔
32
  }
33

34
  for (let i = 0; i < TO_BASE64URL.length; i += 1) {
16✔
35
    charMap[TO_BASE64URL[i].charCodeAt(0)] = i
1,024✔
36
  }
37

38
  return charMap
16✔
39
})()
40

41
/**
42
 * Converts a byte to a Base64-URL string.
43
 *
44
 * @param byte The byte to convert, or null to flush at the end of the byte sequence.
45
 * @param state The Base64 conversion state. Pass an initial value of `{ queue: 0, queuedBits: 0 }`.
46
 * @param emit A function called with the next Base64 character when ready.
47
 */
48
export function byteToBase64URL(
16✔
49
  byte: number | null,
50
  state: { queue: number; queuedBits: number },
51
  emit: (char: string) => void
52
) {
53
  if (byte !== null) {
588✔
54
    state.queue = (state.queue << 8) | byte
570✔
55
    state.queuedBits += 8
570✔
56

57
    while (state.queuedBits >= 6) {
570✔
58
      const pos = (state.queue >> (state.queuedBits - 6)) & 63
754✔
59
      emit(TO_BASE64URL[pos])
754✔
60
      state.queuedBits -= 6
754✔
61
    }
62
  } else if (state.queuedBits > 0) {
18✔
63
    state.queue = state.queue << (6 - state.queuedBits)
12✔
64
    state.queuedBits = 6
12✔
65

66
    while (state.queuedBits >= 6) {
12✔
67
      const pos = (state.queue >> (state.queuedBits - 6)) & 63
12✔
68
      emit(TO_BASE64URL[pos])
12✔
69
      state.queuedBits -= 6
12✔
70
    }
71
  }
72
}
73

74
/**
75
 * Converts a String char code (extracted using `string.charCodeAt(position)`) to a sequence of Base64-URL characters.
76
 *
77
 * @param charCode The char code of the JavaScript string.
78
 * @param state The Base64 state. Pass an initial value of `{ queue: 0, queuedBits: 0 }`.
79
 * @param emit A function called with the next byte.
80
 */
81
export function byteFromBase64URL(
16✔
82
  charCode: number,
83
  state: { queue: number; queuedBits: number },
84
  emit: (byte: number) => void
85
) {
86
  const bits = FROM_BASE64URL[charCode]
13,315✔
87

88
  if (bits > -1) {
13,315✔
89
    // valid Base64-URL character
90
    state.queue = (state.queue << 6) | bits
13,249✔
91
    state.queuedBits += 6
13,249✔
92

93
    while (state.queuedBits >= 8) {
13,249✔
94
      emit((state.queue >> (state.queuedBits - 8)) & 0xff)
9,921✔
95
      state.queuedBits -= 8
9,921✔
96
    }
97
  } else if (bits === -2) {
66✔
98
    // ignore spaces, tabs, newlines, =
99
    return
64✔
100
  } else {
101
    throw new Error(`Invalid Base64-URL character "${String.fromCharCode(charCode)}"`)
2✔
102
  }
103
}
104

105
/**
106
 * Converts a JavaScript string (which may include any valid character) into a
107
 * Base64-URL encoded string. The string is first encoded in UTF-8 which is
108
 * then encoded as Base64-URL.
109
 *
110
 * @param str The string to convert.
111
 */
112
export function stringToBase64URL(str: string) {
16✔
113
  const base64: string[] = []
16✔
114

115
  const emitter = (char: string) => {
16✔
116
    base64.push(char)
758✔
117
  }
118

119
  const state = { queue: 0, queuedBits: 0 }
16✔
120

121
  stringToUTF8(str, (byte: number) => {
16✔
122
    byteToBase64URL(byte, state, emitter)
564✔
123
  })
124

125
  byteToBase64URL(null, state, emitter)
16✔
126

127
  return base64.join('')
16✔
128
}
129

130
/**
131
 * Converts a Base64-URL encoded string into a JavaScript string. It is assumed
132
 * that the underlying string has been encoded as UTF-8.
133
 *
134
 * @param str The Base64-URL encoded string.
135
 */
136
export function stringFromBase64URL(str: string) {
16✔
137
  const conv: string[] = []
56✔
138

139
  const utf8Emit = (codepoint: number) => {
56✔
140
    conv.push(String.fromCodePoint(codepoint))
8,301✔
141
  }
142

143
  const utf8State = {
56✔
144
    utf8seq: 0,
145
    codepoint: 0,
146
  }
147

148
  const b64State = { queue: 0, queuedBits: 0 }
56✔
149

150
  const byteEmit = (byte: number) => {
56✔
151
    stringFromUTF8(byte, utf8State, utf8Emit)
8,571✔
152
  }
153

154
  for (let i = 0; i < str.length; i += 1) {
56✔
155
    byteFromBase64URL(str.charCodeAt(i), b64State, byteEmit)
11,507✔
156
  }
157

158
  return conv.join('')
54✔
159
}
160

161
/**
162
 * Converts a Unicode codepoint to a multi-byte UTF-8 sequence.
163
 *
164
 * @param codepoint The Unicode codepoint.
165
 * @param emit      Function which will be called for each UTF-8 byte that represents the codepoint.
166
 */
167
export function codepointToUTF8(codepoint: number, emit: (byte: number) => void) {
16✔
168
  if (codepoint <= 0x7f) {
1,784✔
169
    emit(codepoint)
1,634✔
170
    return
1,634✔
171
  } else if (codepoint <= 0x7ff) {
150✔
172
    emit(0xc0 | (codepoint >> 6))
30✔
173
    emit(0x80 | (codepoint & 0x3f))
30✔
174
    return
30✔
175
  } else if (codepoint <= 0xffff) {
120✔
176
    emit(0xe0 | (codepoint >> 12))
114✔
177
    emit(0x80 | ((codepoint >> 6) & 0x3f))
114✔
178
    emit(0x80 | (codepoint & 0x3f))
114✔
179
    return
114✔
180
  } else if (codepoint <= 0x10ffff) {
6✔
181
    emit(0xf0 | (codepoint >> 18))
4✔
182
    emit(0x80 | ((codepoint >> 12) & 0x3f))
4✔
183
    emit(0x80 | ((codepoint >> 6) & 0x3f))
4✔
184
    emit(0x80 | (codepoint & 0x3f))
4✔
185
    return
4✔
186
  }
187

188
  throw new Error(`Unrecognized Unicode codepoint: ${codepoint.toString(16)}`)
2✔
189
}
190

191
/**
192
 * Converts a JavaScript string to a sequence of UTF-8 bytes.
193
 *
194
 * @param str  The string to convert to UTF-8.
195
 * @param emit Function which will be called for each UTF-8 byte of the string.
196
 */
197
export function stringToUTF8(str: string, emit: (byte: number) => void) {
16✔
198
  for (let i = 0; i < str.length; i += 1) {
18✔
199
    let codepoint = str.charCodeAt(i)
1,782✔
200

201
    if (codepoint > 0xd7ff && codepoint <= 0xdbff) {
1,782✔
202
      // most UTF-16 codepoints are Unicode codepoints, except values in this
203
      // range where the next UTF-16 codepoint needs to be combined with the
204
      // current one to get the Unicode codepoint
205
      const highSurrogate = ((codepoint - 0xd800) * 0x400) & 0xffff
4✔
206
      const lowSurrogate = (str.charCodeAt(i + 1) - 0xdc00) & 0xffff
4✔
207
      codepoint = (lowSurrogate | highSurrogate) + 0x10000
4✔
208
      i += 1
4✔
209
    }
210

211
    codepointToUTF8(codepoint, emit)
1,782✔
212
  }
213
}
214

215
/**
216
 * Converts a UTF-8 byte to a Unicode codepoint.
217
 *
218
 * @param byte  The UTF-8 byte next in the sequence.
219
 * @param state The shared state between consecutive UTF-8 bytes in the
220
 *              sequence, an object with the shape `{ utf8seq: 0, codepoint: 0 }`.
221
 * @param emit  Function which will be called for each codepoint.
222
 */
223
export function stringFromUTF8(
16✔
224
  byte: number,
225
  state: { utf8seq: number; codepoint: number },
226
  emit: (codepoint: number) => void
227
) {
228
  if (state.utf8seq === 0) {
8,587✔
229
    if (byte <= 0x7f) {
8,313✔
230
      emit(byte)
8,153✔
231
      return
8,153✔
232
    }
233

234
    // count the number of 1 leading bits until you reach 0
235
    for (let leadingBit = 1; leadingBit < 6; leadingBit += 1) {
160✔
236
      if (((byte >> (7 - leadingBit)) & 1) === 0) {
460✔
237
        state.utf8seq = leadingBit
158✔
238
        break
158✔
239
      }
240
    }
241

242
    if (state.utf8seq === 2) {
160✔
243
      state.codepoint = byte & 31
32✔
244
    } else if (state.utf8seq === 3) {
128✔
245
      state.codepoint = byte & 15
116✔
246
    } else if (state.utf8seq === 4) {
12✔
247
      state.codepoint = byte & 7
4✔
248
    } else {
249
      throw new Error('Invalid UTF-8 sequence')
8✔
250
    }
251

252
    state.utf8seq -= 1
152✔
253
  } else if (state.utf8seq > 0) {
274✔
254
    if (byte <= 0x7f) {
274✔
255
      throw new Error('Invalid UTF-8 sequence')
4✔
256
    }
257

258
    state.codepoint = (state.codepoint << 6) | (byte & 63)
270✔
259
    state.utf8seq -= 1
270✔
260

261
    if (state.utf8seq === 0) {
270✔
262
      emit(state.codepoint)
148✔
263
    }
264
  }
265
}
266

267
/**
268
 * Helper functions to convert different types of strings to Uint8Array
269
 */
270

271
export function base64UrlToUint8Array(str: string): Uint8Array {
16✔
272
  const result: number[] = []
19✔
273
  const state = { queue: 0, queuedBits: 0 }
19✔
274

275
  const onByte = (byte: number) => {
19✔
276
    result.push(byte)
1,350✔
277
  }
278

279
  for (let i = 0; i < str.length; i += 1) {
19✔
280
    byteFromBase64URL(str.charCodeAt(i), state, onByte)
1,808✔
281
  }
282

283
  return new Uint8Array(result)
19✔
284
}
285

286
export function stringToUint8Array(str: string): Uint8Array {
16✔
287
  const result: number[] = []
2✔
288
  stringToUTF8(str, (byte: number) => result.push(byte))
1,488✔
289
  return new Uint8Array(result)
2✔
290
}
291

292
export function bytesToBase64URL(bytes: Uint8Array) {
16✔
293
  const result: string[] = []
2✔
294
  const state = { queue: 0, queuedBits: 0 }
2✔
295

296
  const onChar = (char: string) => {
2✔
297
    result.push(char)
8✔
298
  }
299

300
  bytes.forEach((byte) => byteToBase64URL(byte, state, onChar))
6✔
301

302
  // always call with `null` after processing all bytes
303
  byteToBase64URL(null, state, onChar)
2✔
304

305
  return result.join('')
2✔
306
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc