This is my first time using erlang and I decided to try and write a wrapper for an API. Here's what I've got so far:-
-module(my_api_wrapper).
%% API exports
-export([auth/0]).
auth() ->
application:start(inets),
application:start(ssl),
AuthStr = base64:encode_to_string("username:password"),
Method = post,
URL = "https://api.endpoint.com/auth",
Header = [{"Authorization", "Basic " ++ AuthStr}],
Type = "application/json",
Body = "{\"grant_type\":\"client_credentials\"}",
HTTPOptions = [],
Options = [],
httpc:request(Method, {URL, Header, Type, Body}, HTTPOptions, Options).
When testing this at the shell I get an error:-
{error,{failed_connect,[{to_address,{"api.endpoint.com",
443}},
{inet,[inet],closed}]}}
I can't figure out what I'm doing wrong here! I'm running this version Erlang/OTP 19 [erts-8.0.2]. Any help appreciated.
For anyone who it might help - here's exactly what I changed to make the code in my original question work - thanks to Dogbert for his comment above.
-module(my_api_wrapper).
%% API exports
-export([auth/0]).
auth() ->
application:start(inets),
application:start(ssl),
AuthStr = base64:encode_to_string("username:password"),
Method = post,
URL = "https://api.endpoint.com/auth",
Header = [{"Authorization", "Basic " ++ AuthStr}],
Type = "application/json",
Body = "{\"grant_type\":\"client_credentials\"}",
% ADD SSL CONFIG BELOW!
HTTPOptions = [{ssl,[{versions, ['tlsv1.2']}]}],
Options = [],
httpc:request(Method, {URL, Header, Type, Body}, HTTPOptions, Options).
Related
I am 100% sure that my client-id and client-secret are valid. I used it in my python code and it just worked fine
local http = require("coro-http")
local json = require("json")
local url = "https://id.twitch.tv/oauth2/token"
local client_id = "<>"
local client_secret = "<>"
local headers = {
["Content-Type"] = "application/x-www-form-urlencoded"
}
local body = "client_id=" .. client_id .. "&client_secret=" .. client_secret .. "&grant_type=client_credentials"
local response, w = http.request("POST", url, headers, body)
print(w)
local data = json.decode(w)
local access_token = data.access_token
local headers = {
["Client-ID"] = client_id,
["Authorization"] = "Bearer " .. access_token
}
local response, b = http.request("GET", "https://api.twitch.tv/helix/channels?broadcaster_id=141981764", headers)
print(b)
Getting token and then do a simple get request
I found this repository which is doing exactly what you're trying to.
From the code you provided and the one from the above repo, I would say #LMD comment is the way to go. You need to urlencode your body string.
Maybe querystring from luvit could be a good starting point.
I am new to power BI and power query, I am trying to get the JSon data from Post rest api. But i always get a 400 error. Where am I going wrong.
let
authkey ="Bearer xxxx",
url = "https://example.com/xxx",
body = "{""objectId"":""settlement_entity"",""queryString"":""?itemrefs=settlementreference,itemdescription&perpage=12&includekeyitems=true&includeforeignkeyitems=true&includetimestamp=true&includeadditionalmeta=true""}",
Source = Json.Document(Web.Contents(url,
[
Headers = [#"Authorization"= authkey,#"Content-Type"="application/json"], Content = Text.ToBinary(body)
]
))
in
Source
Ok I fixed the issue the body was missing square bracket
let
authkey ="Bearer xxxx",
url = "https://example.com/xxx",
body = "[{""objectId"":""settlement_entity"",""queryString"":""?itemrefs=settlementreference,itemdescription&perpage=12&includekeyitems=true&includeforeignkeyitems=true&includetimestamp=true&includeadditionalmeta=true""]}",
Source = Json.Document(Web.Contents(url,
[
Headers = [#"Authorization"= authkey,#"Content-Type"="application/json"], Content = Text.ToBinary(body)
]
))
in
Source
I am making a HTTP call.
local headers_value = { Content-Type = "multipart/form-data",
Accept = "application/json",
Authorization = "Basic ccccc==" }
The error I am getting is (Line 3 is the above code)
Program starting as
'"C:\Users\idoladmin\Downloads\ZeroBraneStudio\bin\lua53_win64\lua53.exe"
-e "io.stdout:setvbuf('no')" "C:\Code\Lua\send_calltoCM.lua"'. Program 'lua53.exe' started in 'C:\Code\Lua' (pid: 3452).
C:\Users\idoladmin\Downloads\ZeroBraneStudio\bin\lua53_win64\lua53.exe:
C:\Code\Lua\send_calltoCM.lua:3: '}' expected near '=' Program
completed in 0.05 seconds (pid: 3452).
What am I missing?
As mentioned in comments, you can't use Content-Type directly in a table constructor because it's not an identifier.
You can use the [] syntax, like this:
local headers_value = { ['Content-Type'] = "multipart/form-data",
Accept = "application/json",
Authorization = "Basic ccccc==" }
The use of an identifier such as Accept is syntactic sugar for ['Accept'], just as headers_value.Accept is syntactic sugar for headers_value['Accept'].
Hello I am writing oauth 2 library to access google api's and my code is as follows
jwt_create() ->
{ok,PemBin} = file:read_file("your-key-file.pem"),
PemEntry = public_key:pem_decode(PemBin),
[A,B] = PemEntry,
io:format("A:: ~p ~n",[A]),
PrivateKey = public_key:pem_entry_decode(PemEntry),
JwtHeaderJson = encode_json(jwt_header()),
JwtClaimsetJson = encode_json(jwt_claimset()),
ComputeSignature = compute_signature(JwtHeaderJson, JwtClaimsetJson, PrivateKey),
Z=binary:replace(
binary:replace(<<JwtHeaderJson/binary, ".", JwtClaimsetJson/binary, ".", ComputeSignature/binary>>,
<<"+">>, <<"-">>, [global]),
<<"/">>, <<"_">>, [global]),
io:format("JWT:: ~p ~n",[Z]).
compute_signature(Header, ClaimSet,#'RSAPrivateKey'{publicExponent=Exponent
,modulus=Modulus
,privateExponent=PrivateExponent}) ->
base64:encode(crypto:sign(rsa, sha256, <<Header/binary, ".", ClaimSet/binary>>,
[Exponent, Modulus, PrivateExponent])).
encode_json(JWToken) ->
base64:encode(jsx:encode(JWToken)).
I am getting error as follows:
exception error: no function clause matching
public_key:pem_entry_decode([{'PrivateKeyInfo',<<48,130,4,191,2,1,0,48,13,6,9,42,134,
72,134,247,13,1,1,1,5,0,4,130,4,...>>,
not_encrypted},
{'Certificate',<<48,130,3,96,48,130,2,72,160,3,2,1,2,2,8,
79,59,244,35,60,15,3,155,48,...>>,
not_encrypted}]) (public_key.erl, line 123)
in function googleoauth:jwt_create/0 (src/googleoauth.erl, line 55)
Please help me in generating JWS and JWT for OAUTH 2 for accessing google apis
You are passing the wrong thing to public_key:pem_entry_decode/1:
This will resolve your problem:
PrivateKey = public_key:pem_entry_decode(A),
public_key:pem_entry_decode/1 takes a single pem_entry() but a PEM file can contain many entries, perhaps your code PemEntry = public_key:pem_decode(PemBin) should read PemEntries = public_key:pem_decode(PemBin) instead?
Also note the line before assumes 2 list entries, you might have meant this instead (not sure your intent here though)?
[A|B] = PemEntry,
I am trying to extend this library https://github.com/tim/erlang-oauth-examples to allow the client to make multipart posts (as necessary for Twitter's statuses/update_with_media). So far the, this is my attempt at the extra final signing and authentication logic that is necessary. The formation of the Body, using Boundaries is done elsewhere, and I don't think is the stumbling block.
oauth_post({multipart, Boundary}, URL, Body, Consumer, Token, TokenSecret) ->
BodyH = base64:encode_to_string(crypto:hash(sha, Body)),
Signed = oauth:sign("POST", URL, [{"oauth_body_hash", BodyH}], Consumer, Token, TokenSecret),
{[[{ "oauth_signature", Sig}],
[{"oauth_body_hash", BBody}]], Rest} =
proplists:split(Signed, ["oauth_signature", "oauth_body_hash"]),
Encoded = [ {"oauth_signature", oauth:uri_encode(Sig)}
, {"oauth_body_hash", oauth:uri_encode(BBody)}
| Rest],
Sorted = lists:sort(Encoded),
Auth = lists:flatten(string:join([ K++"=\""++V++"\"" || {K,V} <- Sorted], ", ")),
OAuth = "OAuth " ++ Auth,
ContentType = "multipart/form-data;boundary=" ++ Boundary,
Headers = [ {"Authorization", OAuth}
, {"Content-Type", ContentType}
, {"Content-Length", integer_to_list(length(Body))}],
Request = {URL, Headers, ContentType, Body},
httpc:request(post, Request, [], []).
But so far this method call fails to Authenticate. Can anyone, with this domain expertise, see what I'm doing wrong? Many thanks.
After Answer
Per Paul's answer below, this is what I ended up using. Updated my fork of the library too.
oauth_post({multipart, Boundary}, URL, Body, Consumer, Token, TokenSecret) ->
BodyH = base64:encode_to_string(crypto:hash(sha, Body)),
Signed = oauth:sign("POST", URL, [{"oauth_body_hash", BodyH}]
, Consumer, Token, TokenSecret),
{AuthorizationParams, []} =
lists:partition(fun({K, _}) -> lists:prefix("oauth_", K) end, Signed),
Headers = [ oauth:header(AuthorizationParams)],
ContentType = "multipart/form-data;boundary=" ++ Boundary,
Request = {URL, Headers, ContentType, Body},
httpc:request(post, Request, [], []).
The issue is that your code only URI-encodes the signature and the body hash. All oauth_* parameters must be URI-encoded, and especially oauth_nonce which is a Base64 string in this OAuth library.
Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth
As a side note:
Fetching the value for key "oauth_body_hash" does not make sense since you passed this value in the first place (it is in BodyH).
You don't need to sort the oauth_ parameters in the signature.
You don't need the Content-Type and Content-Length headers, inets' httpc will add them for you unless you pass headers_as_is option.
Simply do:
oauth_post({multipart, Boundary}, URL, Body, Consumer, Token, TokenSecret) ->
BodyH = base64:encode_to_string(crypto:hash(sha, Body)),
Signed = oauth:sign("POST", URL, [{"oauth_body_hash", BodyH}], Consumer, Token, TokenSecret),
% URI encode values returned by oauth:sign/6 for the Authorization header
Auth = lists:flatten(string:join([ K++"=\""++oauth:uri_encode(V)++"\"" || {K,V} <- Signed], ", ")),
OAuth = "OAuth " ++ Auth,
ContentType = "multipart/form-data;boundary=" ++ Boundary,
Headers = [ {"Authorization", OAuth} ],
Request = {URL, Headers, ContentType, Body},
httpc:request(post, Request, [], []).