ddo / oauth-1.0a

OAuth 1.0a Request Authorization for Node and Browser
MIT License
325 stars 116 forks source link

Documentation and support for WebCrypto #108

Open macintoshhelper opened 3 years ago

macintoshhelper commented 3 years ago

Hi, WebCrypto usage will generally require the usage of asynchronous APIs. The oauth.authorize function does not support promises for the signature hash function.

I've opened a PR over at #107 which should resolve this, and after that, documentation could be added to the README for a WebCrypto example to replace the CryptoJS, potentially resolving #97

Ref #107

johtso commented 2 years ago

@macintoshhelper hey, did you ever get WebCrypto signing working?

I spent a bunch of hours trying to write a signature function to use with your fork, but I could never get the signature to validate..

This is the non-working code I ended up with:

import OAuth from 'oauth-1.0a';

const CALLBACK_URL = "https://localhost:3000/oauth/callback";

const encoder = new TextEncoder();

async function signData(baseString: string, keyString: string) {
  return await crypto.subtle.importKey(
    'raw',
    encoder.encode(keyString),
    { name: 'HMAC', hash: 'SHA-1' },
    false,
    ['sign']
  ).then(key => {
    return crypto.subtle.sign(
      "HMAC",
      key,
      encoder.encode(baseString)
    );
  }).then(signature => {
    let b = new Uint8Array(signature);
    // base64 digest
    return btoa(String.fromCharCode(...b));
  });
}

export async function getRequestToken(consumerKey: string, consumerSecret: string) {
  const url = "https://www.flickr.com/services/oauth/request_token";
  const token = {
    key: consumerKey,
    secret: consumerSecret
  }
  const oauth = new OAuth({
    consumer: token,
    signature_method: 'HMAC-SHA1',
    hash_function: signData,
  });
  const requestData = {
    url,
    method: 'GET',
    data: {
      oauth_callback: CALLBACK_URL
    }
  };

  const authorisedRequest = await oauth.authorizeAsync(requestData, token);
  let params = new URLSearchParams();
  for (let [key, value] of Object.entries(authorisedRequest)) {
    params.append(key, value as string);
  }
  const response = await fetch(requestData.url + `?${params}`, {
    method: requestData.method,
  });

  const body = await response.text();
  const parsedBody = oauth.deParam(body);
  return parsedBody;
}
lumnn commented 11 months ago

I've got this working with following code and pull request #118

const oauth = new OAuth({
  consumer: {
    key,
    secret
  },
  signature_method: 'HMAC-SHA256',
  async hash_function(baseString, key) {
    // encoder to convert string to Uint8Array
    var enc = new TextEncoder();

    const cryptKey = await crypto.subtle.importKey(
      'raw',
      enc.encode(key),
      { name: 'HMAC': hash: 'SHA-256' },
      false,
      ['sign', 'verify'],
    )

    const signature = await crypto.subtle.sign(
      { name: 'HMAC': hash: 'SHA-256' },
      cryptKey,
      enc.encode(baseString)
    )

    let b = new Uint8Array(signature);
    // base64 digest
    return btoa(String.fromCharCode(...b));
  }
})

const auth = await oauth.authorize(
  {
    method: method.toUpperCase(),
    url,
  },
  {
    key: this.options.accessToken,
    secret: this.options.tokenSecret
  }
)