Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use custom base64 decoder to workaround deno bug #406

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/auth/scram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ export async function executeScram(
return continueScramConversation(cryptoMethod, result, authContext);
}

function decodeBase64(b64: string): Uint8Array {
const b64Web = b64.replace(/-/g, "+").replace(/_/g, "/");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are converting a URL Safe base64 to an actual base64?

so you issue is that you don't actually use a base64 string, but you use a url safe base64 adoptation.

base64 uses / and + by default. so the current impl is not wrong. we just expect actual base64 strings

do you require a url safe base64? / is this per mongodb driver spec?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a really good question lucsoft, but I don't have the answers unfortunately.

I have managed to create a small script to recreate the problem:

import { MongoClient } from '../mod.ts';

for (let x = 0; x < 100000; x++) {
  const client = new MongoClient();

  const url = `mongodb://testadmin1:testpass1@localhost:27017?directConnection=true`
  console.log('url:', url);
  await client.connect(url);

  await client.close()
  console.log('');
}

Run that, and within a few seconds it will throw:

deno-mongo % deno run --allow-net demo/main.ts
url: mongodb://testadmin1:testpass1@localhost:27017?directConnection=true

url: mongodb://testadmin1:testpass1@localhost:27017?directConnection=true

url: mongodb://testadmin1:testpass1@localhost:27017?directConnection=true



url: mongodb://testadmin1:testpass1@localhost:27017?directConnection=true
error: Uncaught (in promise) Error: MongoError: "Connection failed: Failed to decode base64"
      throw new MongoDriverError(`Connection failed: ${e.message || e}`);
            ^
    at MongoClient.connect (file:///deno-mongo/src/client.ts:46:13)
    at eventLoopTick (ext:core/01_core.js:178:11)
    at async file:///deno-mongo/demo/main.ts:8:3

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not sure what's going on, but if I edit the scram.ts file

Screenshot 2023-12-04 at 20 47 35

Then run my script above, I get:

url: mongodb://testadmin1:testpass1@localhost:27017?directConnection=true
parsedResponse= { v: "" }
decodeBase64 Uint8Array(0) []

url: mongodb://testadmin1:testpass1@localhost:27017?directConnection=true
parsedResponse= { v: "" }
decodeBase64 Uint8Array(0) []

url: mongodb://testadmin1:testpass1@localhost:27017?directConnection=true
parsedResponse= { v: "XwftqO5acqzP3qfL6HMwI" }
error: Uncaught (in promise) InvalidCharacterError: Failed to decode base64
  const binString = atob(b64);
                    ^
    at atob (ext:deno_web/05_base64.js:28:16)
    at Module.decode (https://deno.land/std@0.202.0/encoding/base64.ts:138:21)
    at continueScramConversation (file://deno-mongo/src/auth/scram.ts:206:35)
    at eventLoopTick (ext:core/01_core.js:178:11)
    at async Cluster.authenticateToServer (file://deno-mongo/src/cluster.ts:86:7)
    at async Promise.all (index 0)
    at async Cluster.authenticate (file://deno-mongo/src/cluster.ts:55:23)
    at async MongoClient.connect (file://deno-mongo/src/client.ts:37:7)
    at async file://deno-mongo/demo/main.ts:8:3

It's strange, the ones before have a blank parsedResponse, seem to work. But when data get's returned it fails. What is that data?

const binString = atob(b64Web);
const size = binString.length;
const bytes = new Uint8Array(size);
for (let i = 0; i < size; i++) {
bytes[i] = binString.charCodeAt(i);
}
return bytes;
}

export async function continueScramConversation(
cryptoMethod: CryptoMethod,
response: Document,
Expand Down Expand Up @@ -193,7 +204,7 @@ export async function continueScramConversation(
);
if (
!compareDigest(
b64.decode(parsedResponse.v),
decodeBase64(parsedResponse.v),
new Uint8Array(serverSignature),
)
) {
Expand Down