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

Deno Deploy Support #26

Open
oscartbeaumont opened this issue Feb 14, 2022 · 16 comments
Open

Deno Deploy Support #26

oscartbeaumont opened this issue Feb 14, 2022 · 16 comments

Comments

@oscartbeaumont
Copy link

Currently hash and compare are not supported when deploying code on Deno Deploy. This is due to Worker not being defined refer to upstream issue.

However, until Worker is implemented with Deno Deploy you can use the hashSync and compareSync methods.

I have included a helper below which allows for easily switching between these two methods for anyone working on a codebase designed to run efficiently locally and inside Deno Deploy.

import {
  hash as hashPromise,
  hashSync,
  compare as comparePromise,
  compareSync,
} from "https://deno.land/x/bcrypt/mod.ts";

export const isRunningInDenoDeploy = Deno.permissions?.query === undefined; // This is crude check for if the code in running in Deno Deploy. It works for now but may not work in the future.

export const hash: typeof hashPromise = isRunningInDenoDeploy
  ? (plaintext: string, salt: string | undefined = undefined) =>
      new Promise((res) => res(hashSync(plaintext, salt)))
  : hashPromise;
export const compare: typeof comparePromise = isRunningInDenoDeploy
  ? (plaintext: string, hash: string) =>
      new Promise((res) => res(compareSync(plaintext, hash)))
  : comparePromise;

I primarily created this issue to help anyone who runs into the same issue. It would be cool to see the helpers I included added to this library but I think this would be unlikely given how hacky it is.

@l3onh4rd
Copy link

l3onh4rd commented Mar 7, 2022

Wow, nice! I was here to create a ticket that it isn't running on Deno Deploy. Thank you very much for your help and work ;)

@JamesBroadberry
Copy link
Owner

Thanks for raising the issue, I'm sure it'll help a lot of people out. However, this operation is blocking so running it non-async isn't the intended use case and can cause issues (e.g. if many requests hit a server at once) so while it may work for you, I don't think adding it to the library would be wise.

I'll close the issue if/when deno-deploy supports workers (please let me know since I don't follow deno-deploy closely) and leave it open for now so others can find it. Thanks again for sharing your workaround!

@psi-4ward
Copy link

This issues hits also deno compile. Anyone has a way to detect this case?

@ralyodio

This comment was marked as off-topic.

@oscartbeaumont

This comment was marked as off-topic.

Yarilo added a commit to Yarilo/azar that referenced this issue Jul 6, 2022
@obskur123
Copy link

Love you dawg, i was just searching for this

@KorigamiK
Copy link

Thank you so much man, this really helped !

Currently hash and compare are not supported when deploying code on Deno Deploy. This is due to Worker not being defined refer to upstream issue.

However, until Worker is implemented with Deno Deploy you can use the hashSync and compareSync methods.

I have included a helper below which allows for easily switching between these two methods for anyone working on a codebase designed to run efficiently locally and inside Deno Deploy.

import {
  hash as hashPromise,
  hashSync,
  compare as comparePromise,
  compareSync,
} from "https://deno.land/x/bcrypt/mod.ts";

export const isRunningInDenoDeploy = Deno.permissions?.query === undefined; // This is crude check for if the code in running in Deno Deploy. It works for now but may not work in the future.

export const hash: typeof hashPromise = isRunningInDenoDeploy
  ? (plaintext: string, salt: string | undefined = undefined) =>
      new Promise((res) => res(hashSync(plaintext, salt)))
  : hashPromise;
export const compare: typeof comparePromise = isRunningInDenoDeploy
  ? (plaintext: string, hash: string) =>
      new Promise((res) => res(compareSync(plaintext, hash)))
  : comparePromise;

I primarily created this issue to help anyone who runs into the same issue. It would be cool to see the helpers I included added to this library but I think this would be unlikely given how hacky it is.

@KorigamiK
Copy link

isRunningInDenoDeploy should be checked through the environment variables in the project.

@AshwinDeTaeye
Copy link

Is there any status update concerning this?
SInce deno deploy has the web crypto API, it might be the Worker never will be implemented.

Is there a way we could use the web crypto API instead of the sync ways with bcrypt?

@JamesBroadberry
Copy link
Owner

@AshwinDeTaeye This is a problem with Deno Deploy not supporting Workers. If you need to run your app on Deno Deploy, it seems that the only option right now is to have these calls blocking and use the sync methods.

I'm not quite sure what you mean by using the Web Crypto API instead of Workers.

Upstream issue previously mentioned here: denoland/deploy_feedback#171

@AshwinDeTaeye
Copy link

Deno deploy does support the Web Crypto API. https://deno.com/deploy/docs/runtime-api
I suppose there should be async ways of achieving hash and compare through this API.

What i found: https://bradyjoslin.com/blog/encryption-webcrypto/

Do you think this could be done with this API?

https://doc.deno.land/deno/stable/~/SubtleCrypto

@JamesBroadberry
Copy link
Owner

To be honest, I've not really kept up with Deno anymore and I'm not prepared to overhaul this code just to support Deno Deploy because it doesn't support Workers. If you do manage to find a way, please do keep us posted so others interested in the thread know. Thanks 😄

@AshwinDeTaeye
Copy link

AshwinDeTaeye commented Oct 5, 2022

We can use the standard lib from Deno, Web Crypto, to be able to do async hashing.
Following worked for me in Deno deploy:

const buff_to_base64 = (buff: number[] | Uint8Array) =>
  btoa(String.fromCharCode.apply(null, buff));

const base64_to_buf = (b64: string) =>
  Uint8Array.from(atob(b64), (c) => c.charCodeAt(null));

const enc = new TextEncoder();
const dec = new TextDecoder();

const getPasswordKey = async (password: any) =>
  await window.crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    "PBKDF2",
    false,
    ['deriveBits', "deriveKey"]
  );

const deriveKey = async (passwordKey: any, salt: any, keyUsage: any[]) =>
  await window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt,
      iterations: 250000,
      hash: "SHA-256",
    },
    passwordKey,
    { name: "AES-GCM", length: 256 },
    false,
    keyUsage
  );

export async function encryptData(secretData: string | undefined, password: any) {
  try {
    const salt = window.crypto.getRandomValues(new Uint8Array(16));
    const iv = window.crypto.getRandomValues(new Uint8Array(12));
    const passwordKey = await getPasswordKey(password);
    const aesKey = await deriveKey(passwordKey, salt, ["encrypt"]);
    const encryptedContent = await window.crypto.subtle.encrypt(
      {
        name: "AES-GCM",
        iv: iv,
      },
      aesKey,
      enc.encode(secretData)
    );

    const encryptedContentArr = new Uint8Array(encryptedContent);
    let buff = new Uint8Array(
      salt.byteLength + iv.byteLength + encryptedContentArr.byteLength
    );
    buff.set(salt, 0);
    buff.set(iv, salt.byteLength);
    buff.set(encryptedContentArr, salt.byteLength + iv.byteLength);
    const base64Buff = buff_to_base64(buff);
    return base64Buff;
  } catch (e) {
    console.log(`Error - ${e}`);
    return "";
  }
}

export async function decryptData(
  encryptedData: string,
  password: string | undefined
) {
  try {
    const encryptedDataBuff = base64_to_buf(encryptedData);
    const salt = encryptedDataBuff.slice(0, 16);
    const iv = encryptedDataBuff.slice(16, 16 + 12);
    const data = encryptedDataBuff.slice(16 + 12);
    const passwordKey = await getPasswordKey(password);
    const aesKey = await deriveKey(passwordKey, salt, ["decrypt"]);
    const decryptedContent = await window.crypto.subtle.decrypt(
      {
        name: "AES-GCM",
        iv: iv,
      },
      aesKey,
      data
    );
    return dec.decode(decryptedContent);
  } catch (e) {
    console.log(`Error - ${e}`);
    return "";
  }
}

@AL1L
Copy link

AL1L commented Feb 7, 2023

For the proposed solution in the OP, I think a more sensible way to detect if it'll work is by using

// deno-lint-ignore no-explicit-any
export const isRunningInDenoDeploy = (globalThis as any).Worker === undefined;

@derikvanschaik
Copy link

derikvanschaik commented Apr 15, 2023

For the proposed solution in the OP, I think a more sensible way to detect if it'll work is by using

// deno-lint-ignore no-explicit-any
export const isRunningInDenoDeploy = (globalThis as any).Worker === undefined;

^ This suggestion worked for me, however an alternative solution could be to set an environment variable such as Deno.env.Get('DENO_ENV') === 'production'

@AL1L
Copy link

AL1L commented Apr 24, 2023

For the proposed solution in the OP, I think a more sensible way to detect if it'll work is by using

// deno-lint-ignore no-explicit-any
export const isRunningInDenoDeploy = (globalThis as any).Worker === undefined;

^ This suggestion worked for me, however an alternative solution could be to set an environment variable such as Deno.env.Get('DENO_ENV') === 'production'

That's actually what I ended up going with, because of another library that I needed to know which environment I was in. Just didn't post it here lol

J053Fabi0 added a commit to J053Fabi0/Ayn-Rand-Frases-Bot-Telegram that referenced this issue Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants