Edits history of script submission #23056 for ' Get Access Token (Service Account) (gworkspace)'

  • bunnative
    One script reply has been approved by the moderators
    Ap­pro­ved
    //native
    
    const b64url = (bytes: Uint8Array) =>
      btoa(String.fromCharCode(...bytes))
        .replace(/\+/g, "-")
        .replace(/\//g, "_")
        .replace(/=+$/, "")
    
    /**
     * Get Access Token (Service Account)
     * Mint a one-hour Admin SDK access token from a service account with domain-wide delegation, impersonating a Workspace admin. Returns `{ token, expires_in }`, which can be passed as the Google Workspace resource of any other script in a flow. The scopes must be authorized for the service account's client ID in Admin console > Security > API controls > Domain-wide delegation.
     */
    export async function main(
      service_account: RT.GcpServiceAccount,
      admin_email: string,
      scopes: string[] = [
        "https://www.googleapis.com/auth/admin.directory.user",
        "https://www.googleapis.com/auth/admin.directory.group",
        "https://www.googleapis.com/auth/admin.directory.orgunit",
      ],
    ) {
      const tokenUri = service_account.token_uri || "https://oauth2.googleapis.com/token"
      const iat = Math.floor(Date.now() / 1000)
      const enc = new TextEncoder()
      const unsigned = [
        { alg: "RS256", typ: "JWT" },
        {
          iss: service_account.client_email,
          sub: admin_email,
          scope: scopes.join(" "),
          aud: tokenUri,
          iat,
          exp: iat + 3600,
        },
      ]
        .map((part) => b64url(enc.encode(JSON.stringify(part))))
        .join(".")
    
      const pem = service_account.private_key
        .replace(/\\n/g, "\n")
        .replace(/-----[A-Z ]+-----/g, "")
        .replace(/\s/g, "")
      const key = await crypto.subtle.importKey(
        "pkcs8",
        Uint8Array.from(atob(pem), (c) => c.charCodeAt(0)),
        { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
        false,
        ["sign"],
      )
      const signature = new Uint8Array(
        await crypto.subtle.sign("RSASSA-PKCS1-v1_5", key, enc.encode(unsigned)),
      )
    
      const response = await fetch(tokenUri, {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
          Accept: "application/json",
        },
        body: new URLSearchParams({
          grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
          assertion: `${unsigned}.${b64url(signature)}`,
        }),
      })
    
      if (!response.ok) {
        throw new Error(`${response.status} ${await response.text()}`)
      }
    
      const { access_token, expires_in } = (await response.json()) as {
        access_token: string
        expires_in: number
      }
      return { token: access_token, expires_in }
    }
    

    Submitted by hugo989 1 hour ago