diff options
author | Alexey Shchepin <alexey@process-one.net> | 2019-10-03 06:18:07 +0300 |
---|---|---|
committer | Alexey Shchepin <alexey@process-one.net> | 2019-10-03 06:18:48 +0300 |
commit | 5d549dca96c643345ba92e67504e67eb1b6b0681 (patch) | |
tree | 8228edfd0595643c869420d1506e76ebf5c45146 /src/ejabberd_oauth.erl | |
parent | 949e71efb644f6b79030e26ed880bb2e74b93da0 (diff) |
Check redirect_uri for OAUTH implicit grant
Diffstat (limited to 'src/ejabberd_oauth.erl')
-rw-r--r-- | src/ejabberd_oauth.erl | 131 |
1 files changed, 94 insertions, 37 deletions
diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl index 4060b4b7b..27e211f13 100644 --- a/src/ejabberd_oauth.erl +++ b/src/ejabberd_oauth.erl @@ -50,7 +50,9 @@ -export([get_commands_spec/0, oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1, - oauth_add_client/3, oauth_remove_client/1]). + oauth_add_client_password/3, + oauth_add_client_implicit/3, + oauth_remove_client/1]). -include("xmpp.hrl"). -include("logger.hrl"). @@ -65,6 +67,11 @@ -callback lookup(binary()) -> {ok, #oauth_token{}} | error. -callback clean(non_neg_integer()) -> any(). +-record(oauth_ctx, { + password :: binary() | admin_generated, + client :: #oauth_client{} + }). + %% There are two ways to obtain an oauth token: %% * Using the web form/api results in the token being generated in behalf of the user providing the user/pass %% * Using the command line and oauth_issue_token command, the token is generated in behalf of ejabberd' sysadmin @@ -99,12 +106,21 @@ get_commands_spec() -> result = {tokens, {list, {token, {tuple, [{token, string}, {user, string}, {scope, string}, {expires_in, string}]}}}}, result_desc = "List of remaining tokens" }, - #ejabberd_commands{name = oauth_add_client, tags = [oauth], - desc = "Add OAUTH client_id", - module = ?MODULE, function = oauth_add_client, + #ejabberd_commands{name = oauth_add_client_password, tags = [oauth], + desc = "Add OAUTH client_id with password grant type", + module = ?MODULE, function = oauth_add_client_password, + args = [{client_id, binary}, + {client_name, binary}, + {secret, binary}], + policy = restricted, + result = {res, restuple} + }, + #ejabberd_commands{name = oauth_add_client_implicit, tags = [oauth], + desc = "Add OAUTH client_id with implicit grant type", + module = ?MODULE, function = oauth_add_client_implicit, args = [{client_id, binary}, - {secret, binary}, - {grant_type, binary}], + {client_name, binary}, + {redirect_uri, binary}], policy = restricted, result = {res, restuple} }, @@ -146,18 +162,21 @@ oauth_revoke_token(Token) -> ok = mnesia:dirty_delete(oauth_token, list_to_binary(Token)), oauth_list_tokens(). -oauth_add_client(Client, Secret, SGrantType) -> - case SGrantType of - <<"password">> -> - DBMod = get_db_backend(), - DBMod:store_client(#oauth_client{client = Client, - secret = Secret, - grant_type = password, - options = []}), - {ok, []}; - _ -> - {error, "Unsupported grant type"} - end. +oauth_add_client_password(ClientID, ClientName, Secret) -> + DBMod = get_db_backend(), + DBMod:store_client(#oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = password, + options = [{secret, Secret}]}), + {ok, []}. + +oauth_add_client_implicit(ClientID, ClientName, RedirectURI) -> + DBMod = get_db_backend(), + DBMod:store_client(#oauth_client{client_id = ClientID, + client_name = ClientName, + grant_type = implicit, + options = [{redirect_uri, RedirectURI}]}), + {ok, []}. oauth_remove_client(Client) -> DBMod = get_db_backend(), @@ -216,9 +235,23 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -get_client_identity(Client, Ctx) -> {ok, {Ctx, {client, Client}}}. +get_client_identity({client, ClientID}, Ctx) -> + {ok, {Ctx, {client, ClientID}}}. -verify_redirection_uri(_, _, Ctx) -> {ok, Ctx}. +verify_redirection_uri(_ClientID, RedirectURI, Ctx) -> + case Ctx of + #oauth_ctx{client = #oauth_client{grant_type = implicit} = Client} -> + case get_redirect_uri(Client) of + RedirectURI -> + {ok, Ctx}; + _ -> + {error, invalid_uri} + end; + #oauth_ctx{client = #oauth_client{}} -> + {error, invalid_client}; + _ -> + {ok, Ctx} + end. authenticate_user({User, Server}, Ctx) -> case jid:make(User, Server) of @@ -228,15 +261,16 @@ authenticate_user({User, Server}, Ctx) -> case acl:match_rule(JID#jid.lserver, Access, JID) of allow -> case Ctx of - {password, Password} -> - case ejabberd_auth:check_password(User, <<"">>, Server, Password) of - true -> + #oauth_ctx{password = admin_generated} -> {ok, {Ctx, {user, User, Server}}}; - false -> - {error, badpass} - end; - admin_generated -> - {ok, {Ctx, {user, User, Server}}} + #oauth_ctx{password = Password} + when is_binary(Password) -> + case ejabberd_auth:check_password(User, <<"">>, Server, Password) of + true -> + {ok, {Ctx, {user, User, Server}}}; + false -> + {error, badpass} + end end; deny -> {error, badpass} @@ -245,7 +279,20 @@ authenticate_user({User, Server}, Ctx) -> {error, badpass} end. -authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}. +authenticate_client(ClientID, Ctx) -> + case ejabberd_option:oauth_client_id_check() of + allow -> + {ok, {Ctx, {client, ClientID}}}; + deny -> {error, not_allowed}; + db -> + DBMod = get_db_backend(), + case DBMod:lookup_client(ClientID) of + {ok, #oauth_client{} = Client} -> + {ok, {Ctx#oauth_ctx{client = Client}, {client, ClientID}}}; + _ -> + {error, not_allowed} + end + end. -spec verify_resowner_scope({user, binary(), binary()}, [binary()], any()) -> {ok, any(), [binary()]} | {error, any()}. @@ -525,7 +572,7 @@ process(_Handlers, ClientId, RedirectURI, Scope, - {password, Password}) of + #oauth_ctx{password = Password}) of {ok, {_AppContext, Authorization}} -> {ok, {_AppContext2, Response}} = oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]), @@ -597,13 +644,18 @@ process(_Handlers, end, DBMod = get_db_backend(), case DBMod:lookup_client(ClientID) of - {ok, #oauth_client{secret = Secret} = Client} -> - case proplists:get_value(<<"grant_type">>, Q, <<"">>) of - <<"password">> when - Client#oauth_client.grant_type == password -> - password; + {ok, #oauth_client{grant_type = password} = Client} -> + case get_client_secret(Client) of + Secret -> + case proplists:get_value(<<"grant_type">>, Q, <<"">>) of + <<"password">> when + Client#oauth_client.grant_type == password -> + password; + _ -> + unsupported_grant_type + end; _ -> - unsupported_grant_type + deny end; _ -> deny @@ -623,7 +675,7 @@ process(_Handlers, end, case oauth2:authorize_password({Username, Server}, Scope, - {password, Password}) of + #oauth_ctx{password = Password}) of {ok, {_AppContext, Authorization}} -> {ok, {_AppContext2, Response}} = oauth2:issue_token(Authorization, [{expiry_time, ExpiresIn} || ExpiresIn /= undefined ]), @@ -663,6 +715,11 @@ get_db_backend() -> DBType = ejabberd_option:oauth_db_type(), list_to_existing_atom("ejabberd_oauth_" ++ atom_to_list(DBType)). +get_client_secret(#oauth_client{grant_type = password, options = Options}) -> + proplists:get_value(secret, Options, false). + +get_redirect_uri(#oauth_client{grant_type = implicit, options = Options}) -> + proplists:get_value(redirect_uri, Options, false). %% Headers as per RFC 6749 json_response(Code, Body) -> |