aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ejabberd_oauth.hrl6
-rw-r--r--sql/lite.sql4
-rw-r--r--sql/mysql.new.sql7
-rw-r--r--sql/mysql.sql4
-rw-r--r--sql/pg.new.sql7
-rw-r--r--sql/pg.sql4
-rw-r--r--src/ejabberd_oauth.erl131
-rw-r--r--src/ejabberd_oauth_rest.erl42
-rw-r--r--src/ejabberd_oauth_sql.erl40
9 files changed, 167 insertions, 78 deletions
diff --git a/include/ejabberd_oauth.hrl b/include/ejabberd_oauth.hrl
index 9254da1e5..2daee39d9 100644
--- a/include/ejabberd_oauth.hrl
+++ b/include/ejabberd_oauth.hrl
@@ -26,8 +26,8 @@
}).
-record(oauth_client, {
- client = <<"">> :: binary() | '_',
- secret = <<"">> :: binary() | '_',
- grant_type = password :: password | '_',
+ client_id = <<"">> :: binary() | '_',
+ client_name = <<"">> :: binary() | '_',
+ grant_type :: password | implicit | '_',
options :: [any()] | '_'
}).
diff --git a/sql/lite.sql b/sql/lite.sql
index c77922c20..0b6bb12c1 100644
--- a/sql/lite.sql
+++ b/sql/lite.sql
@@ -339,8 +339,8 @@ CREATE TABLE oauth_token (
);
CREATE TABLE oauth_client (
- client text PRIMARY KEY,
- secret text NOT NULL,
+ client_id text PRIMARY KEY,
+ client_name text NOT NULL,
grant_type text NOT NULL,
options text NOT NULL
);
diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql
index b2f9acfd4..15843fc7d 100644
--- a/sql/mysql.new.sql
+++ b/sql/mysql.new.sql
@@ -384,6 +384,13 @@ CREATE TABLE oauth_token (
expire bigint NOT NULL
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+CREATE TABLE oauth_client (
+ client_id varchar(191) NOT NULL PRIMARY KEY,
+ client_name text NOT NULL,
+ grant_type text NOT NULL,
+ options text NOT NULL
+) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
CREATE TABLE route (
domain text NOT NULL,
server_host varchar(191) NOT NULL,
diff --git a/sql/mysql.sql b/sql/mysql.sql
index 7f415a2e4..7afc2cf1a 100644
--- a/sql/mysql.sql
+++ b/sql/mysql.sql
@@ -355,8 +355,8 @@ CREATE TABLE oauth_token (
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE oauth_client (
- client varchar(191) NOT NULL PRIMARY KEY,
- secret text NOT NULL,
+ client_id varchar(191) NOT NULL PRIMARY KEY,
+ client_name text NOT NULL,
grant_type text NOT NULL,
options text NOT NULL
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/sql/pg.new.sql b/sql/pg.new.sql
index c585fd307..1a3d52316 100644
--- a/sql/pg.new.sql
+++ b/sql/pg.new.sql
@@ -529,6 +529,13 @@ CREATE TABLE oauth_token (
CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token);
+CREATE TABLE oauth_client (
+ client_id text PRIMARY KEY,
+ client_name text NOT NULL,
+ grant_type text NOT NULL,
+ options text NOT NULL
+);
+
CREATE TABLE route (
domain text NOT NULL,
server_host text NOT NULL,
diff --git a/sql/pg.sql b/sql/pg.sql
index 0f87fd5d8..34d9a4867 100644
--- a/sql/pg.sql
+++ b/sql/pg.sql
@@ -359,8 +359,8 @@ CREATE TABLE oauth_token (
CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token);
CREATE TABLE oauth_client (
- client text PRIMARY KEY,
- secret text NOT NULL,
+ client_id text PRIMARY KEY,
+ client_name text NOT NULL,
grant_type text NOT NULL,
options text NOT NULL
);
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) ->
diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl
index b15fc904b..6af67996b 100644
--- a/src/ejabberd_oauth_rest.erl
+++ b/src/ejabberd_oauth_rest.erl
@@ -91,22 +91,25 @@ path(Path) ->
Base = ejabberd_option:ext_api_path_oauth(),
<<Base/binary, "/", Path/binary>>.
-store_client(#oauth_client{client = Client,
- secret = Secret,
- grant_type = GrantType} = R) ->
+store_client(#oauth_client{client_id = ClientID,
+ client_name = ClientName,
+ grant_type = GrantType,
+ options = Options} = R) ->
Path = path(<<"store_client">>),
- %% Retry 2 times, with a backoff of 500millisec
SGrantType =
case GrantType of
- password -> <<"password">>
+ password -> <<"password">>;
+ implicit -> <<"implicit">>
end,
+ SOptions = misc:term_to_base64(Options),
+ %% Retry 2 times, with a backoff of 500millisec
case rest:with_retry(
post,
[ejabberd_config:get_myname(), Path, [],
- {[{<<"client">>, Client},
- {<<"secret">>, Secret},
+ {[{<<"client_id">>, ClientID},
+ {<<"client_name">>, ClientName},
{<<"grant_type">>, SGrantType},
- {<<"options">>, []}
+ {<<"options">>, SOptions}
]}], 2, 500) of
{ok, Code, _} when Code == 200 orelse Code == 201 ->
ok;
@@ -115,22 +118,29 @@ store_client(#oauth_client{client = Client,
{error, db_failure}
end.
-lookup_client(Client) ->
+lookup_client(ClientID) ->
Path = path(<<"lookup_client">>),
case rest:with_retry(post, [ejabberd_config:get_myname(), Path, [],
- {[{<<"client">>, Client}]}],
+ {[{<<"client_id">>, ClientID}]}],
2, 500) of
{ok, 200, {Data}} ->
- Secret = proplists:get_value(<<"secret">>, Data, <<>>),
+ ClientName = proplists:get_value(<<"client_name">>, Data, <<>>),
SGrantType = proplists:get_value(<<"grant_type">>, Data, <<>>),
GrantType =
case SGrantType of
- <<"password">> -> password
+ <<"password">> -> password;
+ <<"implicit">> -> implicit
end,
- {ok, #oauth_client{client = Client,
- secret = Secret,
- grant_type = GrantType,
- options = []}};
+ SOptions = proplists:get_value(<<"options">>, Data, <<>>),
+ case misc:base64_to_term(SOptions) of
+ {term, Options} ->
+ {ok, #oauth_client{client_id = ClientID,
+ client_name = ClientName,
+ grant_type = GrantType,
+ options = Options}};
+ _ ->
+ error
+ end;
{ok, 404, _Resp} ->
error;
Other ->
diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl
index 724017af4..fb91a8813 100644
--- a/src/ejabberd_oauth_sql.erl
+++ b/src/ejabberd_oauth_sql.erl
@@ -83,37 +83,45 @@ clean(TS) ->
ejabberd_config:get_myname(),
?SQL("delete from oauth_token where expire < %(TS)d")).
-lookup_client(Client) ->
+lookup_client(ClientID) ->
case ejabberd_sql:sql_query(
ejabberd_config:get_myname(),
- ?SQL("select @(secret)s, @(grant_type)s"
- " from oauth_client where client=%(Client)s")) of
- {selected, [{Secret, SGrantType}]} ->
+ ?SQL("select @(client_name)s, @(grant_type)s, @(options)s"
+ " from oauth_client where client_id=%(ClientID)s")) of
+ {selected, [{ClientName, SGrantType, SOptions}]} ->
GrantType =
case SGrantType of
- <<"password">> -> password
+ <<"password">> -> password;
+ <<"implicit">> -> implicit
end,
- {ok, #oauth_client{client = Client,
- secret = Secret,
- grant_type = GrantType,
- options = []}};
+ case misc:base64_to_term(SOptions) of
+ {term, Options} ->
+ {ok, #oauth_client{client_id = ClientID,
+ client_name = ClientName,
+ grant_type = GrantType,
+ options = Options}};
+ _ ->
+ error
+ end;
_ ->
error
end.
-store_client(#oauth_client{client = Client,
- secret = Secret,
- grant_type = GrantType}) ->
+store_client(#oauth_client{client_id = ClientID,
+ client_name = ClientName,
+ grant_type = GrantType,
+ options = Options}) ->
SGrantType =
case GrantType of
- password -> <<"password">>
+ password -> <<"password">>;
+ implicit -> <<"implicit">>
end,
- SOptions = <<"">>,
+ SOptions = misc:term_to_base64(Options),
case ?SQL_UPSERT(
ejabberd_config:get_myname(),
"oauth_client",
- ["!client=%(Client)s",
- "secret=%(Secret)s",
+ ["!client_id=%(ClientID)s",
+ "client_name=%(ClientName)s",
"grant_type=%(SGrantType)s",
"options=%(SOptions)s"]) of
ok ->