defmodule Oauth do
# 6789973 Acct# for NetSuite Support
@acct_id 6_789_973
@base_uri "https://#{@acct_id}.suitetalk.api.netsuite.com/services/rest/record/v1/"
@oauth_creds [
realm: "#{@acct_id}",
oauth_consumer_key: "1a1a4402b8a1dd876e67352e12b1a94ba6c5620f15d349f101b0059c13ecf030",
oauth_consumer_secret: "949462be662ece983c8cf8a79e96c0901d2d8d2efd584f4c82ba9ab5c4ad3c8b",
oauth_token: "df92b02bffec93f03c0f3454437cd5115efad796da37e90321f4ea02732d59e2",
oauth_token_secret: "db60928d4387dabc626d23ce01fa5b68c19fd645871b35ba925608052d65262c",
oauth_signature_method: "HMAC-SHA256",
oauth_version: "1.0"
]
def forge_oauth_head(method, url, qs \\ []) do
timestamp = DateTime.utc_now() |> DateTime.to_unix() |> Integer.to_string()
seed = :crypto.strong_rand_bytes(16) |> Base.encode16()
nonce = :crypto.hash(:md5, seed) |> Base.encode16(case: :lower) |> String.slice(0..12)
head =
(@oauth_creds ++ [oauth_timestamp: timestamp, oauth_nonce: nonce])
|> Enum.reject(fn {key, _} ->
[:oauth_consumer_secret, :oauth_token_secret] |> Enum.member?(key)
end)
|> Enum.reduce([], fn {key, value}, acc ->
acc ++ ["#{key}=\"#{value |> URI.encode_www_form()}\""]
end)
|> Enum.join(",")
{"Authorization",
"OAuth #{head}" <>
",oauth_signature=\"" <>
forge_sign(method <> "&" <> (url |> URI.encode_www_form()), timestamp, nonce, qs) <> "\""}
end
def forge_sign(head, timestamp, nonce, qs) do
params =
(@oauth_creds ++ [oauth_timestamp: timestamp, oauth_nonce: nonce] ++ qs)
|> Enum.reject(fn {key, _} ->
[:realm, :oauth_consumer_secret, :oauth_token_secret] |> Enum.member?(key)
end)
|> Enum.sort_by(fn {key, val} ->
{key, val}
end)
|> Enum.reduce([], fn {key, value}, acc ->
acc ++ ["#{key}=#{value |> URI.encode(&URI.char_unreserved?(&1))}"]
end)
|> Enum.join("&")
|> URI.encode_www_form()
key =
(@oauth_creds |> Keyword.fetch!(:oauth_consumer_secret)) <>
"&" <> (@oauth_creds |> Keyword.fetch!(:oauth_token_secret))
:crypto.mac(:hmac, :sha256, key, head <> "&" <> params)
|> Base.encode64(case: :lower)
|> URI.encode_www_form()
end
end
Oauth.forge_oauth_head("GET", "https://pyroclasti.cloud", username: "cigzigwon")
defmodule OauthTest do
use ExUnit.Case
@acct_id 6_789_973
@customer_uri "https://#{@acct_id}.suitetalk.api.netsuite.com/services/rest/record/v1/customer"
test "forge_sign/4 can return an oauth_signature using required header params" do
timestamp = DateTime.utc_now() |> DateTime.to_unix() |> Integer.to_string()
seed = :crypto.strong_rand_bytes(16) |> Base.encode16()
nonce = :crypto.hash(:md5, seed) |> Base.encode16(case: :lower) |> String.slice(0..12)
oauth_signature =
Oauth.forge_sign(
"POST&#{@customer_uri |> URI.encode_www_form()}",
timestamp,
nonce,
[]
)
assert oauth_signature |> String.length() >= 46
end
test "forge_sign/4 can return an oauth_signature using qs params" do
timestamp = DateTime.utc_now() |> DateTime.to_unix() |> Integer.to_string()
seed = :crypto.strong_rand_bytes(16) |> Base.encode16()
nonce = :crypto.hash(:md5, seed) |> Base.encode16(case: :lower) |> String.slice(0..12)
oauth_signature =
Oauth.forge_sign(
"POST&#{@customer_uri |> URI.encode_www_form()}",
timestamp,
nonce,
username: "cigzigwon"
)
assert oauth_signature |> String.length() >= 46
end
test "forge_oauth_head/3 can return an Authorization tuple header for OAuth 1.0a" do
{"Authorization", value} = Oauth.forge_oauth_head("POST", @customer_uri)
assert value |> String.contains?("oauth_consumer_secret") == false
assert value |> String.contains?("oauth_token_secret") == false
end
end