Skip to content

Commit

Permalink
Merge pull request #17 from kivra/fetch-access-token-on-request
Browse files Browse the repository at this point in the history
feat: fetch access token on request
  • Loading branch information
alexandre-kivra authored Nov 1, 2019
2 parents d5a0320 + 9dc00ba commit 6be972c
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/erlang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ jobs:
run: make
- name: Run xref
run: make xref
- name: Run common tests
run: make ct
- name: Run elvis
run: make elvis_rock


dialyze:

runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@

{deps, [{restc, ".*", {git, "git://github.com/kivra/restclient.git", {tag, "0.7.1"}}}]}.

{profiles,
[{test, [
{erl_opts, [nowarn_export_all]},
{deps, [ {meck, "0.8.13"}
]}
]}
]}.

{erl_opts, [
warnings_as_errors,
warn_export_all
Expand Down
40 changes: 38 additions & 2 deletions src/oauth2c.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

-module(oauth2c).

-export([client/4]).
-export([client/5]).

-export([retrieve_access_token/4]).
-export([retrieve_access_token/5]).
-export([retrieve_access_token/6]).
Expand Down Expand Up @@ -85,6 +88,29 @@
-type client() :: #client{}.

%%% API ========================================================================

-spec client(Type, URL, ID, Secret) -> client() when
Type :: at_type(),
URL :: url(),
ID :: binary(),
Secret :: binary().
client(Type, URL, ID, Secret) ->
client(Type, URL, ID, Secret, undefined).

-spec client(Type, URL, ID, Secret, Scope) -> client() when
Type :: at_type(),
URL :: url(),
ID :: binary(),
Secret :: binary(),
Scope :: binary() | undefined.
client(Type, URL, ID, Secret, Scope) ->
#client{ grant_type = Type
, auth_url = URL
, id = ID
, secret = Secret
, scope = Scope
}.

-spec retrieve_access_token(Type, URL, ID, Secret) ->
{ok, Headers::headers(), client()} | {error, Reason :: binary()} when
Type :: at_type(),
Expand Down Expand Up @@ -301,10 +327,20 @@ get_token_type(Type) ->
get_str_token_type("bearer") -> bearer;
get_str_token_type(_Else) -> unsupported.

do_request(Method, Type, Url, Expect, Headers, Body, Options, Client) ->
Headers2 = add_auth_header(Headers, Client),
do_request(Method, Type, Url, Expect, Headers, Body, Options, Client0) ->
{Headers2, Client} = add_auth_header(Headers, Client0, Options),
{restc:request(Method, Type, Url, Expect, Headers2, Body, Options), Client}.

add_auth_header(Headers0,
#client{access_token = undefined} = Client0,
Options) ->
{ok, _RetrHeaders, Client} = do_retrieve_access_token(Client0, Options),
Headers = add_auth_header(Headers0, Client),
{Headers, Client};
add_auth_header(Headers0, Client, _) ->
Headers = add_auth_header(Headers0, Client),
{Headers, Client}.

add_auth_header(Headers, #client{grant_type = <<"azure_client_credentials">>,
access_token = AccessToken}) ->
AH = {<<"Authorization">>, <<"bearer ", AccessToken/binary>>},
Expand Down
100 changes: 100 additions & 0 deletions test/oauth2c_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
-module(oauth2c_SUITE).
-compile([export_all]).

-include_lib("common_test/include/ct.hrl").
-include_lib("stdlib/include/assert.hrl").

-define(AUTH_URL, <<"https://authurl.com">>).
-define(INVALID_TOKEN_AUTH_URL, <<"https://invalidauthurl.com">>).
-define(REQUEST_URL, <<"https://requesturl.com">>).
-define(CLIENT_CREDENTIALS_GRANT, <<"client_credentials">>).
-define(VALID_TOKEN, <<"iamanaccesstoken">>).
-define(HEADERS(AccessToken),
[{<<"Authorization">>, <<"bearer ", AccessToken/binary>>}]).

-define(GET_BODY, [{<<"a">>, <<"b">>}]).

groups() -> [].

all() -> [ retrieve_access_token
, fetch_access_token_on_request
, fetch_access_token_on_request
, fetch_new_token_on_401
].

init_per_suite(Config) -> Config.
end_per_suite(_Config) -> ok.

init_per_testcase(_TestCase, Config) ->
mock_http_requests(),
Config.
end_per_testcase(_TestCase, Config) ->
meck:unload([restc]),
Config.

retrieve_access_token(_Config) ->
Response = oauth2c:retrieve_access_token(?CLIENT_CREDENTIALS_GRANT,
?AUTH_URL,
<<"ID">>,
<<"SECRET">>),
?assertMatch({ok, _, _}, Response).

fetch_access_token_and_do_request(_Config) ->
{ok, _, Client} = oauth2c:retrieve_access_token(?CLIENT_CREDENTIALS_GRANT,
?AUTH_URL,
<<"ID">>,
<<"SECRET">>),
Response = oauth2c:request(get, json, ?REQUEST_URL, [], [], [], [], Client),
?assertMatch({{ok, 200, _, ?GET_BODY}, Client}, Response),
?assertNot(meck:called(restc, request,
[post, percent, ?AUTH_URL, '_', '_', '_', '_'])).


fetch_access_token_on_request(_Config) ->
Client = oauth2c:client(?CLIENT_CREDENTIALS_GRANT, ?AUTH_URL, <<"ID">>,
<<"SECRET">>),
Response = oauth2c:request(get, json, ?REQUEST_URL, [], [], [], [], Client),
?assertMatch({{ok, 200, _, ?GET_BODY}, _}, Response),
?assert(meck:called(restc, request,
[post, percent, ?AUTH_URL, '_', '_', '_', '_'])).

fetch_new_token_on_401(_Config) ->
{ok, _, Client} = oauth2c:retrieve_access_token(?CLIENT_CREDENTIALS_GRANT,
?INVALID_TOKEN_AUTH_URL,
<<"ID">>,
<<"SECRET">>),
?assert(1 =:= meck:num_calls(restc, request,
[ post, percent,
?INVALID_TOKEN_AUTH_URL, '_', '_', '_', '_'
])),

Response = oauth2c:request(get, json, ?REQUEST_URL, [], [], [], [], Client),
?assertMatch({{ok, 401, _, _}, Client}, Response),
?assert(2 =:= meck:num_calls(restc, request,
[ post, percent,
?INVALID_TOKEN_AUTH_URL, '_', '_', '_', '_'
])).

mock_http_requests() ->
meck:expect(restc, request,
fun(post, percent, ?AUTH_URL, [200], _, _, _) ->
Body = [{<<"access_token">>, ?VALID_TOKEN},
{<<"token_type">>, <<"bearer">>}],
{ok, 200, [], Body};
(post, percent, ?INVALID_TOKEN_AUTH_URL, [200], _, _, _) ->
Body = [{<<"access_token">>, <<"invalid">>},
{<<"token_type">>, <<"bearer">>}],
{ok, 200, [], Body};
(get, json, _, _, Headers, _, _) ->
ValidToken = ?HEADERS(?VALID_TOKEN),
case Headers of
ValidToken -> {ok, 200, [], [{<<"a">>, <<"b">>}]};
_ -> {ok, 401, [], []}
end
end).

%%%_* Editor ===================================================================
%%% Local Variables:
%%% allout-layout: t
%%% erlang-indent-level: 2
%%% End:

1 comment on commit 6be972c

@excode
Copy link

@excode excode commented on 6be972c Nov 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging";
var SCOPES = [MESSAGING_SCOPE];

var http = require('http')

function getAccessToken(){
return new Promise(function(resolve, reject){
var key = require("./ServiceAccountKey.json");
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function(err, tokens){
if(err){
reject(err);
return;
}
resolve(tokens.access_token+":"+tokens.expiry_date);
});
});
}

var server = http.createServer(function(req, res){

 getAccessToken().then(function(access_token){

     res.end(access_token);

 });

});

server.listen(3000, function(){
console.log("Server started");
});


How can I retrieve token from google auth . The above code fetch the token from google auth but i need an ERLANG version; still no luck to achieve the desired result.

Please sign in to comment.