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

base64 decoding with "atob" does not consider web safe base64 encoding in Deno Gmail API Client #19546

Open
MichaelHindley opened this issue Jun 18, 2023 · 1 comment
Labels
triage required 👀 Deno team needs to make a decision if this change is desired web related to Web APIs

Comments

@MichaelHindley
Copy link

Using https://googleapis.deno.dev/v1/gmail:v1.ts by @lucacasonato , the decodeBase64 function calls the built-in "atob". However, the mail parts in Gmail multipart messages are in a different RFC standard base64 encoding (RFC 4648), so attempting to get messages using this client fails with:

error: Uncaught InvalidCharacterError: Failed to decode base64
const binString = atob(b64); 

Normally the workaround would be something like replaceAll("-", "+").replaceAll("_", "/") on the string before passing to atob, but since this is nested deeply in the generated api client, it's not possible.

See https://stackoverflow.com/questions/24812139/base64-decoding-of-mime-email-not-working-gmail-api for other discussion.

I'm not sure if this is the correct place to report this, but the client makes no reference to any issue tracker and I cant find any tests for it, so I went by @lucacasonato as the author and he is a core contributor here.

The message I am testing against, which is a publicly available reply on another repo here on Github, sent as an email. This is the "data" variable that is passed as deserializeMessage(data) in usersMessagesGet.

You can see the base64 data returned from gmail is not compatible with denos atob function that is used in the API Client for gmail.

const data = 'aSBhbSB0b28uIGJ1dCBpIGFtIHVzaW5nIGdvb2dsZSBzaGVldCBPYXV0aC4gSXQncyBub3QgYXV0byByZWZyZXNoIHRva2VuLg0KTm93IGkgYW0gZml4aW5nIGJ5IG1hbnVhbCBmb2xsb3dpbmc6DQpTdGVwIDE6IENsaWNrIHZhcmlhYmxlIGNhdGVnb3J5DQpTdGVwMjogQ2xpY2sgdG8gdG9rZW4gcGF0aA0KU3RlcDM6IFVzaW5nIHNjcmlwdCBjYWxsIHdtaWxsLmdldF92YXJpYWJsZSAodG9rZW5fcGF0aCksIGF0IHRoZSBtb21lbnQgd2luZG1pbGwgaXMgcmV0dXJuaW5nIHRoZSBuZXcgdG9rZW4uIChpZiB5b3UgZG9udCBmb2xsb3cgc3RlcCAxIGFuZCAyLCB0aGUgb2xkIHRva2VuIHdpbGwgYmUgcmV0dXJuDQo8aW1nIHdpZHRoPSIxMzQ1IiBhbHQ9ImltYWd…BzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS93aW5kbWlsbC1sYWJzL3dpbmRtaWxsL2Fzc2V0cy8xMTAzNDUzODEvYjY5ZTllYzQtNjc5Yy00NjViLWE2ZGEtNDY3NDg3NmE3MzM0Ij4NCg0KDQotLSANClJlcGx5IHRvIHRoaXMgZW1haWwgZGlyZWN0bHkgb3IgdmlldyBpdCBvbiBHaXRIdWI6DQpodHRwczovL2dpdGh1Yi5jb20vd2luZG1pbGwtbGFicy93aW5kbWlsbC9pc3N1ZXMvMTczMiNpc3N1ZWNvbW1lbnQtMTU5NTgyOTk4MA0KWW91IGFyZSByZWNlaXZpbmcgdGhpcyBiZWNhdXNlIHlvdSBhdXRob3JlZCB0aGUgdGhyZWFkLg0KDQpNZXNzYWdlIElEOiA8d2luZG1pbGwtbGFicy93aW5kbWlsbC9pc3N1ZXMvMTczMi8xNTk1ODI5OTgwQGdpdGh1Yi5jb20-'

console.log(atob(data))
image

Full data here:

{
  id: "188cab26169338a9",
  threadId: "188c85854acb9b7c",
  labelIds: [
    "IMPORTANT",
    "CATEGORY_PERSONAL",
    "INBOX",
  ],
  snippet: "i am too. but i am using google sheet Oauth. It's not auto refresh token. Now i am fixing by manual following: Step 1: Click variable category Step2: Click to token path Step3: Using script call",
  payload: {
    partId: "",
    mimeType: "multipart/alternative",
    filename: "",
    headers: [
      {
        name: "Delivered-To",
        value: "[email protected]",
      },
      {
        name: "Received",
        value: "by 2002:a05:7300:e123:b0:b0:c9ee:1bfb with SMTP id hd35csp2080813dyb;        Sat, 17 Jun 2023 11:51:08 -0700 (PDT)",
      },
      {
        name: "X-Google-Smtp-Source",
        value: "ACHHUZ7x4Ro+4Oe2DBKoG62YD6M/ePPKFhMFOUTVHJfe0gDu+mdIsgCl4ANvt84Ohi4nIJNIn5ku",
      },
      {
        name: "X-Received",
        value: "by 2002:a05:6214:5008:b0:629:e646:bdad with SMTP id jo8-20020a056214500800b00629e646bdadmr7015944qvb.4.1687027867885;        Sat, 17 Jun 2023 11:51:07 -0700 (PDT)",
      },
      {
        name: "ARC-Seal",
        value: "i=1; a=rsa-sha256; t=1687027867; cv=none;        d=google.com; s=arc-20160816;        b=e7IkuFEzSqYrAiI5jD326aCeL6lCjByljjD8e3WSKOZUqw8MtjGcmfTMI9sJLs1TPk         f1P3cq6jJJha5SXzjd4RSL8phrsQvweA+yUzXjfqqbt6gFswqgFC+JQCXQFf1msvO7h9         YMfNLcEwDlM0ZfRMOl45H8ln4OB3yqRgKwFijUlR36eSvElK7OD38ac26IS9Xh4chUs/         wyeofrTuWBK6WO92M/606LVE6jt5yi3KHYj6F6N/8DLBs8T8anqdyTWc7hF/o3KLBXVZ         HagN9uYdHdIUk5hDOyclBfYygzrExAEI95qTrt76a1owDjozzejF6RCL7l9l6E0pmEhE         4WEA==",
      },
      {
        name: "ARC-Message-Signature",
        value: "i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816;        h=list-unsubscribe:list-post:list-archive:list-id:precedence         :content-transfer-encoding:mime-version:subject:references         :in-reply-to:message-id:cc:to:reply-to:from:date:dkim-signature;        bh=KWzBKdUYyfEMKj433WuD/8hxMEMQK1fsI2IbzqCqT6g=;        b=W5C5w08PnoNtgTiT8RyUvPfAkHBkfZQZVEdYD32EPE3rWrJY4XfCGb7gClD52H1Ogj         ihmqYTz4wMLGLnTpICyHmkCyOn6YhlMVhf5jLFD5QyD8eEDnJQ+KTo6xrFMBXBcc5ShS         yYWLfP6mrq4uRJxblpLiDPkWziIF0CROmMnmHTIALr3YTSMNiwliYm+mObUK/UUuBjX1         aY9H7STwXgScnbZ4v4ljyfONSNiZzcH8mcBIb9P7gFPtTXK7Yhu+iamFfmyXbIo2Pv01         kDHYVW3Py+7nRSRTbrq3MPUAe2xD00kgnav1sMniv02TQRc/l8ivySb6i95lF1nuKdCg         wMtA==",
      },
      {
        name: "ARC-Authentication-Results",
        value: "i=1; mx.google.com;       dkim=pass [email protected] header.s=pf2023 header.b=eocRnNzr;       spf=pass (google.com: domain of [email protected] designates 192.30.252.201 as permitted sender) [email protected];       dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=github.com",
      },
      {
        name: "Return-Path",
        value: "<[email protected]>",
      },
      {
        name: "Received",
        value: "from out-18.smtp.github.com (out-18.smtp.github.com. [192.30.252.201])        by mx.google.com with ESMTPS id jp13-20020ad45f8d000000b006261c92791bsi12464080qvb.316.2023.06.17.11.51.07        for <[email protected]>        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);        Sat, 17 Jun 2023 11:51:07 -0700 (PDT)",
      },
      {
        name: "Received-SPF",
        value: "pass (google.com: domain of [email protected] designates 192.30.252.201 as permitted sender) client-ip=192.30.252.201;",
      },
      {
        name: "Authentication-Results",
        value: "mx.google.com;       dkim=pass [email protected] header.s=pf2023 header.b=eocRnNzr;       spf=pass (google.com: domain of [email protected] designates 192.30.252.201 as permitted sender) [email protected];       dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=github.com",
      },
      {
        name: "Received",
        value: "from github-lowworker-7f00a19.ac4-iad.github.net (github-lowworker-7f00a19.ac4-iad.github.net [10.52.113.68]) by smtp.github.com (Postfix) with ESMTP id 29E35940FD9 for <[email protected]>; Sat, 17 Jun 2023 11:51:07 -0700 (PDT)",
      },
      {
        name: "DKIM-Signature",
        value: "v=1; a=rsa-sha256; c=relaxed/relaxed; d=github.com; s=pf2023; t=1687027867; bh=KWzBKdUYyfEMKj433WuD/8hxMEMQK1fsI2IbzqCqT6g=; h=Date:From:Reply-To:To:Cc:In-Reply-To:References:Subject:List-ID:\t List-Archive:List-Post:List-Unsubscribe:From; b=eocRnNzrEEuaHJBFKfa51fZyMKLf7SzBsFZESFBktjQCgNuJ6JLfmAVLqc6eAQ1il\t HbLwqPiv3pnPm1pH4uinJSAAsbIi8X2/2WRUiBYQDczMpAIbt08q1k4zP6AVheSnwt\t uGnKSyBrfiF+uTO/s00SARMwZSFIujWSSxBcJ3fI=",
      },
      {
        name: "Date",
        value: "Sat, 17 Jun 2023 11:51:07 -0700",
      },
      {
        name: "From",
        value: "royalmaster960 <[email protected]>",
      },
      {
        name: "Reply-To",
        value: "\"windmill-labs/windmill\" <reply+AAHYGP5FV7J26A2BNZBA7MGCTMZRXEVBNHHGSAJUC4@reply.github.com>",
      },
      {
        name: "To",
        value: "\"windmill-labs/windmill\" <[email protected]>",
      },
      {
        name: "Cc",
        value: "MichaelHindley <[email protected]>, Author <[email protected]>",
      },
      {
        name: "Message-ID",
        value: "<windmill-labs/windmill/issues/1732/[email protected]>",
      },
      {
        name: "In-Reply-To",
        value: "<windmill-labs/windmill/issues/[email protected]>",
      },
      {
        name: "References",
        value: "<windmill-labs/windmill/issues/[email protected]>",
      },
      {
        name: "Subject",
        value: "Re: [windmill-labs/windmill] bug: oauth access token for gmail does not auto-refresh (Issue #1732)",
      },
      {
        name: "Mime-Version",
        value: "1.0",
      },
      {
        name: "Content-Type",
        value: "multipart/alternative; boundary=\"--==_mimepart_648e009b1a394_4cbad034874f6\"; charset=UTF-8",
      },
      {
        name: "Content-Transfer-Encoding",
        value: "7bit",
      },
      {
        name: "Precedence",
        value: "list",
      },
      {
        name: "X-GitHub-Sender",
        value: "royalmaster960",
      },
      {
        name: "X-GitHub-Recipient",
        value: "MichaelHindley",
      },
      {
        name: "X-GitHub-Reason",
        value: "author",
      },
      {
        name: "List-ID",
        value: "windmill-labs/windmill <windmill.windmill-labs.github.com>",
      },
      {
        name: "List-Archive",
        value: "https://github.com/windmill-labs/windmill",
      },
      {
        name: "List-Post",
        value: "<mailto:reply+AAHYGP5FV7J26A2BNZBA7MGCTMZRXEVBNHHGSAJUC4@reply.github.com>",
      },
      {
        name: "List-Unsubscribe",
        value: "<mailto:unsub+AAHYGP5FV7J26A2BNZBA7MGCTMZRXEVBNHHGSAJUC4@reply.github.com>, <https://github.com/notifications/unsubscribe/AAHYGP2JWVQJP3GHWXO5MILXLX4BXANCNFSM6AAAAAAZKB4IWE>",
      },
      {
        name: "X-Auto-Response-Suppress",
        value: "All",
      },
      {
        name: "X-GitHub-Recipient-Address",
        value: "[email protected]",
      },
    ],
    body: {
      size: 0,
    },
    parts: [
      {
        partId: "0",
        mimeType: "text/plain",
        filename: "",
        headers: [
          {
            name: "Content-Type",
            value: "text/plain; charset=UTF-8",
          },
          {
            name: "Content-Transfer-Encoding",
            value: "7bit",
          },
        ],
        body: {
          size: 750,
          data: "aSBhbSB0b28uIGJ1dCBpIGFtIHVzaW5nIGdvb2dsZSBzaGVldCBPYXV0aC4gSXQncyBub3QgYXV0byByZWZyZXNoIHRva2VuLg0KTm93IGkgYW0gZml4aW5nIGJ5IG1hbnVhbCBmb2xsb3dpbmc6DQpTdGVwIDE6IENsaWNrIHZhcmlhYmxlIGNhdGVnb3J5DQpTdGVwMjogQ2xpY2sgdG8gdG9rZW4gcGF0aA0KU3RlcDM6IFVzaW5nIHNjcmlwdCBjYWxsIHdtaWxsLmdldF92YXJpYWJsZSAodG9rZW5fcGF0aCksIGF0IHRoZSBtb21lbnQgd2luZG1pbGwgaXMgcmV0dXJuaW5nIHRoZSBuZXcgdG9rZW4uIChpZiB5b3UgZG9udCBmb2xsb3cgc3RlcCAxIGFuZCAyLCB0aGUgb2xkIHRva2VuIHdpbGwgYmUgcmV0dXJuDQo8aW1nIHdpZHRoPSIxMzQ1IiBhbHQ9ImltYWdlIiBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS93aW5kbWlsbC1sYWJzL3dpbmRtaWxsL2Fzc2V0cy8xMTAzNDUzODEvYjY5ZTllYzQtNjc5Yy00NjViLWE2ZGEtNDY3NDg3NmE3MzM0Ij4NCg0KDQotLSANClJlcGx5IHRvIHRoaXMgZW1haWwgZGlyZWN0bHkgb3IgdmlldyBpdCBvbiBHaXRIdWI6DQpodHRwczovL2dpdGh1Yi5jb20vd2luZG1pbGwtbGFicy93aW5kbWlsbC9pc3N1ZXMvMTczMiNpc3N1ZWNvbW1lbnQtMTU5NTgyOTk4MA0KWW91IGFyZSByZWNlaXZpbmcgdGhpcyBiZWNhdXNlIHlvdSBhdXRob3JlZCB0aGUgdGhyZWFkLg0KDQpNZXNzYWdlIElEOiA8d2luZG1pbGwtbGFicy93aW5kbWlsbC9pc3N1ZXMvMTczMi8xNTk1ODI5OTgwQGdpdGh1Yi5jb20-",
        },
      },
      {
        partId: "1",
        mimeType: "text/html",
        filename: "",
        headers: [
          {
            name: "Content-Type",
            value: "text/html; charset=UTF-8",
          },
          {
            name: "Content-Transfer-Encoding",
            value: "7bit",
          },
        ],
        body: {
          size: 2174,
          data: "PHA-PC9wPg0KPHAgZGlyPSJhdXRvIj5pIGFtIHRvby4gYnV0IGkgYW0gdXNpbmcgZ29vZ2xlIHNoZWV0IE9hdXRoLiBJdCdzIG5vdCBhdXRvIHJlZnJlc2ggdG9rZW4uPGJyPg0KTm93IGkgYW0gZml4aW5nIGJ5IG1hbnVhbCBmb2xsb3dpbmc6PGJyPg0KU3RlcCAxOiBDbGljayB2YXJpYWJsZSBjYXRlZ29yeTxicj4NClN0ZXAyOiBDbGljayB0byB0b2tlbiBwYXRoPGJyPg0KU3RlcDM6IFVzaW5nIHNjcmlwdCBjYWxsIHdtaWxsLmdldF92YXJpYWJsZSAodG9rZW5fcGF0aCksIGF0IHRoZSBtb21lbnQgd2luZG1pbGwgaXMgcmV0dXJuaW5nIHRoZSBuZXcgdG9rZW4uIChpZiB5b3UgZG9udCBmb2xsb3cgc3RlcCAxIGFuZCAyLCB0aGUgb2xkIHRva2VuIHdpbGwgYmUgcmV0dXJuPGJyPg0KPGEgdGFyZ2V0PSJfYmxhbmsiIHJlbD0ibm9vcGVuZXIgbm9yZWZlcnJlciIgaHJlZj0iaHR0cHM6Ly91c2VyLWltYWdlcy5naXRodWJ1c2VyY29udGVudC5jb20vMTEwMzQ1MzgxLzI0NjYyNjgwOS1iNjllOWVjNC02NzljLTQ2NWItYTZkYS00Njc0ODc2YTczMzQucG5nIj48aW1nIHdpZHRoPSIxMzQ1IiBhbHQ9ImltYWdlIiBzcmM9Imh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzExMDM0NTM4MS8yNDY2MjY4MDktYjY5ZTllYzQtNjc5Yy00NjViLWE2ZGEtNDY3NDg3NmE3MzM0LnBuZyIgc3R5bGU9Im1heC13aWR0aDogMTAwJTsiPjwvYT48L3A-DQoNCjxwIHN0eWxlPSJmb250LXNpemU6c21hbGw7LXdlYmtpdC10ZXh0LXNpemUtYWRqdXN0Om5vbmU7Y29sb3I6IzY2NjsiPiZtZGFzaDs8YnIgLz5SZXBseSB0byB0aGlzIGVtYWlsIGRpcmVjdGx5LCA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vd2luZG1pbGwtbGFicy93aW5kbWlsbC9pc3N1ZXMvMTczMiNpc3N1ZWNvbW1lbnQtMTU5NTgyOTk4MCI-dmlldyBpdCBvbiBHaXRIdWI8L2E-LCBvciA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vbm90aWZpY2F0aW9ucy91bnN1YnNjcmliZS1hdXRoL0FBSFlHUFpPWkJNS0xHVUlFVkVHSU9MWExYNEJYQU5DTkZTTTZBQUFBQUFaS0I0SVdFIj51bnN1YnNjcmliZTwvYT4uPGJyIC8-WW91IGFyZSByZWNlaXZpbmcgdGhpcyBiZWNhdXNlIHlvdSBhdXRob3JlZCB0aGUgdGhyZWFkLjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vbm90aWZpY2F0aW9ucy9iZWFjb24vQUFIWUdQNDVTU0ZaS01NQVdQQTQ3U0RYTFg0QlhBNUNORlNNNkFBQUFBQVpLQjRJV0dXR0czM05OVlNXNDVDN09SNFhBWk5NSkZaWEc1TEZJTlhXMjNMRk5aMktVWTNQTlZXV0szVFVMNVVXSlRTN0RaWE5ZLmdpZiIgaGVpZ2h0PSIxIiB3aWR0aD0iMSIgYWx0PSIiIC8-PHNwYW4gc3R5bGU9ImNvbG9yOiB0cmFuc3BhcmVudDsgZm9udC1zaXplOiAwOyBkaXNwbGF5OiBub25lOyB2aXNpYmlsaXR5OiBoaWRkZW47IG92ZXJmbG93OiBoaWRkZW47IG9wYWNpdHk6IDA7IHdpZHRoOiAwOyBoZWlnaHQ6IDA7IG1heC13aWR0aDogMDsgbWF4LWhlaWdodDogMDsgbXNvLWhpZGU6IGFsbCI-TWVzc2FnZSBJRDogPHNwYW4-Jmx0O3dpbmRtaWxsLWxhYnMvd2luZG1pbGwvaXNzdWVzLzE3MzIvMTU5NTgyOTk4MDwvc3Bhbj48c3Bhbj5APC9zcGFuPjxzcGFuPmdpdGh1Yjwvc3Bhbj48c3Bhbj4uPC9zcGFuPjxzcGFuPmNvbSZndDs8L3NwYW4-PC9zcGFuPjwvcD4NCjxzY3JpcHQgdHlwZT0iYXBwbGljYXRpb24vbGQranNvbiI-Ww0Kew0KIkBjb250ZXh0IjogImh0dHA6Ly9zY2hlbWEub3JnIiwNCiJAdHlwZSI6ICJFbWFpbE1lc3NhZ2UiLA0KInBvdGVudGlhbEFjdGlvbiI6IHsNCiJAdHlwZSI6ICJWaWV3QWN0aW9uIiwNCiJ0YXJnZXQiOiAiaHR0cHM6Ly9naXRodWIuY29tL3dpbmRtaWxsLWxhYnMvd2luZG1pbGwvaXNzdWVzLzE3MzIjaXNzdWVjb21tZW50LTE1OTU4Mjk5ODAiLA0KInVybCI6ICJodHRwczovL2dpdGh1Yi5jb20vd2luZG1pbGwtbGFicy93aW5kbWlsbC9pc3N1ZXMvMTczMiNpc3N1ZWNvbW1lbnQtMTU5NTgyOTk4MCIsDQoibmFtZSI6ICJWaWV3IElzc3VlIg0KfSwNCiJkZXNjcmlwdGlvbiI6ICJWaWV3IHRoaXMgSXNzdWUgb24gR2l0SHViIiwNCiJwdWJsaXNoZXIiOiB7DQoiQHR5cGUiOiAiT3JnYW5pemF0aW9uIiwNCiJuYW1lIjogIkdpdEh1YiIsDQoidXJsIjogImh0dHBzOi8vZ2l0aHViLmNvbSINCn0NCn0NCl08L3NjcmlwdD4=",
        },
      },
    ],
  },
  sizeEstimate: 8210,
  historyId: "3289341",
  internalDate: "1687027867000",
}

@MichaelHindley
Copy link
Author

MichaelHindley commented Jun 18, 2023

Verified this fix works, but is most likely not the prettiest solution:

function decodeBase64(b64: string): Uint8Array {
  const b64Web = b64.replace(/-/g, "+").replace(/_/g, "/");
  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;
}

By downloading https://googleapis.deno.dev/v1/gmail:v1.ts locally and referencing https://googleapis.deno.dev/_/base@v1/mod.ts for base.

@bartlomieju bartlomieju added triage required 👀 Deno team needs to make a decision if this change is desired web related to Web APIs labels Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage required 👀 Deno team needs to make a decision if this change is desired web related to Web APIs
Projects
None yet
Development

No branches or pull requests

2 participants