From 96ba3c35a26d2bbd75927102a9177df0f2e2eb45 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Mon, 8 Jun 2015 12:58:33 -0300 Subject: [PATCH] OAuth2 Gmail Added OAuth2 GMAIL loggin/logout support. Added OAuth2 Gmail filter. LoginModule Updated LoginModule with OAuth2 Gmail support. Persitence Extended user persitance api with OAuth2 gmail features. (TODO refactor persistance as an user extention) --- .../config/{ => modules}/login/login.json | 0 .../config/modules/login/oauth2_gmail.json | 7 + examples/demo/site/scripts/oauth_gmail.sql | 14 ++ .../modules/login/templates/block_login.tpl | 7 +- modules/login/filter/oauth_gmail_filter.e | 46 +++++ modules/login/login-safe.ecf | 6 + modules/login/login_module.e | 131 ++++++++++++- modules/login/oauth_login_gmail.e | 179 ++++++++++++++++++ src/persistence/user/cms_user_storage_i.e | 21 ++ src/persistence/user/cms_user_storage_null.e | 20 ++ src/persistence/user/cms_user_storage_sql_i.e | 86 ++++++++- src/service/user/cms_user_api.e | 29 +++ 12 files changed, 536 insertions(+), 10 deletions(-) rename examples/demo/site/config/{ => modules}/login/login.json (100%) create mode 100644 examples/demo/site/config/modules/login/oauth2_gmail.json create mode 100644 examples/demo/site/scripts/oauth_gmail.sql create mode 100644 modules/login/filter/oauth_gmail_filter.e create mode 100644 modules/login/oauth_login_gmail.e diff --git a/examples/demo/site/config/login/login.json b/examples/demo/site/config/modules/login/login.json similarity index 100% rename from examples/demo/site/config/login/login.json rename to examples/demo/site/config/modules/login/login.json diff --git a/examples/demo/site/config/modules/login/oauth2_gmail.json b/examples/demo/site/config/modules/login/oauth2_gmail.json new file mode 100644 index 0000000..8cd35ef --- /dev/null +++ b/examples/demo/site/config/modules/login/oauth2_gmail.json @@ -0,0 +1,7 @@ +{ + "api_secret":"ADD_YOUR_SECRET_KEY", + "api_key":"ADD_YOUR_PUBLIC_KEY", + "scope": "email", + "api_revoke":"https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN", + "protected_resource_url":"https://www.googleapis.com/plus/v1/people/me" +} diff --git a/examples/demo/site/scripts/oauth_gmail.sql b/examples/demo/site/scripts/oauth_gmail.sql new file mode 100644 index 0000000..3efbe55 --- /dev/null +++ b/examples/demo/site/scripts/oauth_gmail.sql @@ -0,0 +1,14 @@ +BEGIN; + + +CREATE TABLE "oauth2_gmail"( + "uid" INTEGER PRIMARY KEY NOT NULL CHECK("uid">=0), + "access_token" VARCHAR(255) NOT NULL, + "created" DATETIME NOT NULL, + "details" TEXT NOT NULL, + CONSTRAINT "uid" + UNIQUE("uid") + ); + + +COMMIT; \ No newline at end of file diff --git a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl index 495fdd8..18301e6 100644 --- a/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl +++ b/examples/demo/site/themes/bootstrap/modules/login/templates/block_login.tpl @@ -1,6 +1,6 @@
{if isset="$user"} -

Logout

+

Logout

{/if} {unless isset="$user"}

Login or Register

@@ -28,5 +28,8 @@

- {/unless} + {/unless} +
+ +
\ No newline at end of file diff --git a/modules/login/filter/oauth_gmail_filter.e b/modules/login/filter/oauth_gmail_filter.e new file mode 100644 index 0000000..cd9de8b --- /dev/null +++ b/modules/login/filter/oauth_gmail_filter.e @@ -0,0 +1,46 @@ +note + description: "Summary description for {OAUTH_GMAIL_FILTER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + OAUTH_GMAIL_FILTER + +inherit + WSF_URI_TEMPLATE_HANDLER + CMS_HANDLER + WSF_FILTER + +create + make + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + local + utf: UTF_CONVERTER + do + api.logger.put_debug (generator + ".execute ", Void) +-- if attached req.raw_header_data as l_raw_data then +-- api.logger.put_debug (generator + ".execute " + utf.escaped_utf_32_string_to_utf_8_string_8 (l_raw_data), Void) +-- end + -- A valid user + if + attached {WSF_STRING} req.cookie ("EWF_ROC_OAUTH_GMAIL_SESSION_") as l_roc_auth_session_token + then + if attached {CMS_USER} api.user_api.user_by_oauth2_gmail_token (l_roc_auth_session_token.value) as l_user then + set_current_user (req, l_user) + execute_next (req, res) + else + api.logger.put_error (generator + ".execute login_valid failed for: " + l_roc_auth_session_token.value , Void) + execute_next (req, res) + end + else + api.logger.put_debug (generator + ".execute without authentication", Void) + execute_next (req, res) + end + end + +end diff --git a/modules/login/login-safe.ecf b/modules/login/login-safe.ecf index eaba1d4..38f4ea5 100644 --- a/modules/login/login-safe.ecf +++ b/modules/login/login-safe.ecf @@ -18,6 +18,12 @@ + + + + + + diff --git a/modules/login/login_module.e b/modules/login/login_module.e index 3faceeb..6a95bd2 100644 --- a/modules/login/login_module.e +++ b/modules/login/login_module.e @@ -9,6 +9,7 @@ class inherit CMS_MODULE redefine + filters, register_hooks end @@ -48,6 +49,16 @@ feature {NONE} -- Initialization cache_duration := 0 end + +feature -- Filters + + filters (a_api: CMS_API): detachable LIST [WSF_FILTER] + -- Possibly list of Filter's module. + do + create {ARRAYED_LIST [WSF_FILTER]} Result.make (1) + Result.extend (create {OAUTH_GMAIL_FILTER}.make (a_api)) + end + feature -- Access: docs root_dir: PATH @@ -75,6 +86,8 @@ feature -- Router a_router.handle_with_request_methods ("/new-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_new_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/reset-password", create {WSF_URI_AGENT_HANDLER}.make (agent handle_reset_password (a_api, ?, ?)), a_router.methods_get_post) a_router.handle_with_request_methods ("/roc-logout", create {WSF_URI_AGENT_HANDLER}.make (agent handle_logout (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/login-with-google", create {WSF_URI_AGENT_HANDLER}.make (agent handle_login_with_google (a_api, ?, ?)), a_router.methods_get_post) + a_router.handle_with_request_methods ("/oauthgmail", create {WSF_URI_AGENT_HANDLER}.make (agent handle_callback_gmail (a_api, ?, ?)), a_router.methods_get_post) end feature -- Hooks configuration @@ -171,15 +184,33 @@ feature -- Hooks handle_logout (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) local r: CMS_RESPONSE - br: BAD_REQUEST_ERROR_CMS_RESPONSE l_url: STRING + l_oauth_gmail: OAUTH_LOGIN_GMAIL + l_cookie: WSF_COOKIE do - create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) - r.set_status_code ({HTTP_CONSTANTS}.found) - l_url := req.absolute_script_url ("") - l_url.append ("/basic_auth_logoff") - r.set_redirection (l_url) - r.execute + if + attached {WSF_STRING} req.cookie ("EWF_ROC_OAUTH_GMAIL_SESSION_") as l_cookie_token and then + attached {CMS_USER} current_user (req) as l_user + then + -- Logout gmail + create l_oauth_gmail.make (api, req.absolute_script_url ("")) + l_oauth_gmail.sign_out (l_cookie_token.value) + create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_cookie_token.value) + l_cookie.set_max_age (-1) + res.add_cookie (l_cookie) + unset_current_user (req) + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + r.set_redirection (req.absolute_script_url ("")) + r.execute + else + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_status_code ({HTTP_CONSTANTS}.found) + l_url := req.absolute_script_url ("") + l_url.append ("/basic_auth_logoff") + r.set_redirection (l_url) + r.execute + end end handle_register (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) @@ -606,6 +637,92 @@ feature {NONE} -- Block views end end +feature -- OAuth2 Login with google. + + handle_login_with_google (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_oauth_gmail: OAUTH_LOGIN_GMAIL + do + create l_oauth_gmail.make (api, req.absolute_script_url ("")) + if attached l_oauth_gmail.authorization_url as l_authorization then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + r.set_redirection (l_authorization) + r.execute + else + create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api) + r.set_main_content ("Bad request") + r.execute + end + end + + handle_callback_gmail (api: CMS_API; req: WSF_REQUEST; res: WSF_RESPONSE) + local + r: CMS_RESPONSE + l_auth_gmail: OAUTH_LOGIN_GMAIL + l_user_api: CMS_USER_API + l_user: CMS_USER + l_roles: LIST [CMS_USER_ROLE] + l_cookie: WSF_COOKIE + do + if attached {WSF_STRING} req.query_parameter ("code") as l_code then + create l_auth_gmail.make (api, req.server_url) + l_auth_gmail.sign_request (l_code.value) + if + attached l_auth_gmail.access_token as l_access_token and then + attached l_auth_gmail.user_profile as l_user_profile + then + create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api) + -- extract user email + -- check if the user exist + l_user_api := api.user_api + -- 1 if the user exit put it in the context + if + attached l_auth_gmail.user_email as l_email + then + if attached {CMS_USER} l_user_api.user_by_email (l_email) as p_user then + -- User with email exist + if attached {CMS_USER} l_user_api.user_oauth2_gmail_by_id (p_user.id) then + -- Update oauth entry + l_user_api.update_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + else + -- create a oauth entry + l_user_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, p_user ) + end + create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) + l_cookie.set_max_age (l_access_token.expires_in) + res.add_cookie (l_cookie) + else + + create {ARRAYED_LIST [CMS_USER_ROLE]}l_roles.make (1) + l_roles.force (l_user_api.authenticated_user_role) + + -- Create a new user and oauth entry + create l_user.make (l_email) + l_user.set_email (l_email) + l_user.set_password (new_token) -- generate a random password. + l_user.set_roles (l_roles) + l_user.mark_active + l_user_api.new_user (l_user) + + -- Add oauth entry + l_user_api.new_user_oauth2_gmail (l_access_token.token, l_user_profile, l_user ) + create l_cookie.make ("EWF_ROC_OAUTH_GMAIL_SESSION_", l_access_token.token) + l_cookie.set_max_age (l_access_token.expires_in) + res.add_cookie (l_cookie) + end + else + + + end + r.set_redirection (req.absolute_script_url ("")) + r.execute + end + + end + + end + feature {NONE} -- Token Generation new_token: STRING diff --git a/modules/login/oauth_login_gmail.e b/modules/login/oauth_login_gmail.e new file mode 100644 index 0000000..1c484c9 --- /dev/null +++ b/modules/login/oauth_login_gmail.e @@ -0,0 +1,179 @@ +note + description: "OAuth workflow for Gmails." + date: "$Date$" + revision: "$Revision$" + +class + OAUTH_LOGIN_GMAIL + +inherit + + SHARED_LOGGER + +create + make + +feature {NONE} -- Initialization + + make (a_cms_api:CMS_API a_host: READABLE_STRING_32) + -- Create an object with the host `a_host'. + do + cms_api := a_cms_api + initilize + create config.make_default (api_key, api_secret) + config.set_callback (a_host + "/oauthgmail") + config.set_scope (scope) + create goauth + api_service := goauth.create_service (config) + ensure + cms_api_set: cms_api = a_cms_api + end + + initilize + local + utf: UTF_CONVERTER + do + --Use configuration values if any if not defaul + api_key := "KEY" + api_secret := "SECRET" + scope := "email" + + api_revoke := "[https://accounts.google.com/o/oauth2/revoke?token=$ACCESS_TOKEN]" + protected_resource_url := "https://www.googleapis.com/plus/v1/people/me" + + + if attached {CONFIG_READER} cms_api.module_configuration ("login", "oauth2_gmail") as cfg then + if attached cfg.text_item ("api_secret") as l_api_secret then + api_secret := utf.utf_32_string_to_utf_8_string_8 (l_api_secret) + end + if attached cfg.text_item ("api_key") as l_api_key then + api_key := utf.utf_32_string_to_utf_8_string_8 (l_api_key) + end + if attached cfg.text_item ("scope") as l_scope then + scope := utf.utf_32_string_to_utf_8_string_8 (l_scope) + end + if attached cfg.text_item ("api_revoke") as l_api_revoke then + api_revoke := utf.utf_32_string_to_utf_8_string_8 (l_api_revoke) + end + if attached cfg.text_item ("protected_resource_url") as l_resource_url then + protected_resource_url := utf.utf_32_string_to_utf_8_string_8 (l_resource_url) + end + end + + end + +feature -- Access + + authorization_url: detachable READABLE_STRING_32 + -- Obtain the Authorization URL. + do + -- Obtain the Authorization URL + write_debug_log (generator + ".authorization_url Fetching the Authorization URL..!") + if attached api_service.authorization_url (empty_token) as l_authorization_url then + write_debug_log (generator + ".authorization_url: Got the Authorization URL!") + write_debug_log (generator + ".authorization_url:" + l_authorization_url) + Result := l_authorization_url.as_string_32 + end + end + + sign_request (a_code: READABLE_STRING_32) + -- Sign request with code `a_code'. + --! To get the code `a_code' you need to do a request + --! using the authorization_url + local + request: OAUTH_REQUEST + do + -- Get the access token. + write_debug_log (generator + ".sign_request Fetching the access token with code [" + a_code + "]") + access_token := api_service.access_token_post (empty_token, create {OAUTH_VERIFIER}.make (a_code)) + if attached access_token as l_access_token then + write_debug_log (generator + ".sign_request Got the Access Token [" + l_access_token.debug_output + "]") + -- Get the user email + --! at the moment the scope is mail, but we can change it to get more information. + create request.make ("GET", protected_resource_url) + request.add_header ("Authorization", "Bearer " + l_access_token.token) + api_service.sign_request (l_access_token, request) + if attached {OAUTH_RESPONSE} request.execute as l_response then + write_debug_log (generator + ".sign_request Sign_request response [" + l_response.status.out + "]") + if attached l_response.body as l_body then + user_profile := l_body + write_debug_log (generator + ".sign_request User profile [" + l_body + "]") + end + end + end + end + + sign_out (a_code: READABLE_STRING_32) + -- Invalidate the current OAuth access token `a_code'. + local + l_revoke: STRING + request: OAUTH_REQUEST + do + create l_revoke.make_from_string (api_revoke) + l_revoke.replace_substring_all ("$ACCESS_TOKEN", a_code) + create request.make ("POST", l_revoke) + if attached {OAUTH_RESPONSE} request.execute as l_response then + -- do nothing + write_debug_log (generator + ".sign_out response [" + l_response.status.out + "]") + check invalidate_ok: l_response.status = {HTTP_CONSTANTS}.ok end + end + end + + user_email: detachable READABLE_STRING_32 + -- Retrieve user email if any. + local + l_json: JSON_CONFIG + do + if attached user_profile as l_profile then + create l_json.make_from_string (l_profile) + if + attached {JSON_ARRAY} l_json.item ("emails") as l_array and then + attached {JSON_OBJECT} l_array.i_th (1) as l_object and then + attached {JSON_STRING} l_object.item ("value") as l_email + then + Result := l_email.item + end + end + end + +feature -- Access + + access_token: detachable OAUTH_TOKEN + -- JSON representing the access token. + + user_profile: detachable READABLE_STRING_32 + -- JSON representing the user profiles. + +feature {NONE} -- Implementation + + goauth: OAUTH_20_GOOGLE_API + -- OAuth 2.0 Google API. + + config: OAUTH_CONFIG + -- configuration. + + api_service: OAUTH_SERVICE_I + -- Service. + + api_key: STRING + -- public key. + + api_secret: STRING + -- secret key. + + scope: STRING + -- api scope to access protected resources. + + api_revoke: STRING + -- Revoke url + + protected_resource_url: STRING + -- Resource url. + + empty_token: detachable OAUTH_TOKEN + -- fake token. + + cms_api: CMS_API + -- CMS API. + +end diff --git a/src/persistence/user/cms_user_storage_i.e b/src/persistence/user/cms_user_storage_i.e index 44b794a..777767f 100644 --- a/src/persistence/user/cms_user_storage_i.e +++ b/src/persistence/user/cms_user_storage_i.e @@ -178,4 +178,25 @@ feature -- Change: User password recovery -- . deferred end + +feature -- Change: User Oauth2 + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + deferred + end + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Update user `a_user' with oauth2 gmail authentication. + deferred + end + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + deferred + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + deferred + end + end diff --git a/src/persistence/user/cms_user_storage_null.e b/src/persistence/user/cms_user_storage_null.e index 8142699..cb6c12b 100644 --- a/src/persistence/user/cms_user_storage_null.e +++ b/src/persistence/user/cms_user_storage_null.e @@ -107,4 +107,24 @@ feature -- Change: User password recovery -- . do end + +feature -- Change User Oauth + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + do + end + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Update user `a_user' with oauth2 gmail authentication. + do + end + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + do + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + do + end end diff --git a/src/persistence/user/cms_user_storage_sql_i.e b/src/persistence/user/cms_user_storage_sql_i.e index 1950668..2f100d7 100644 --- a/src/persistence/user/cms_user_storage_sql_i.e +++ b/src/persistence/user/cms_user_storage_sql_i.e @@ -579,6 +579,82 @@ feature -- Change: User password recovery end +feature -- User Oauth2 + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_oauth2_gmail") + create l_parameters.make (4) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (a_user_profile, "profile") + l_parameters.put (create {DATE_TIME}.make_now_utc, "utc_date") + + sql_change (sql_insert_oauth2_gmail, l_parameters) + sql_commit_transaction + end + + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + sql_begin_transaction + + write_information_log (generator + ".new_user_oauth2_gmail") + create l_parameters.make (4) + l_parameters.put (a_user.id, "uid") + l_parameters.put (a_token, "token") + l_parameters.put (a_user_profile, "profile") + + sql_change (sql_update_oauth2_gmail, l_parameters) + sql_commit_transaction + end + + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + -- User for the given password token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_by_oauth2_gmail_token") + create l_parameters.make (1) + l_parameters.put (a_token, "token") + sql_query (select_user_by_oauth2_gmail_token, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + -- User for the given password token `a_token', if any. + local + l_parameters: STRING_TABLE [detachable ANY] + do + error_handler.reset + write_information_log (generator + ".user_oauth2_gmail_by_id") + create l_parameters.make (1) + l_parameters.put (a_uid, "uid") + sql_query (select_user_oauth2_gmail_by_id, l_parameters) + if sql_rows_count = 1 then + Result := fetch_user + else + check no_more_than_one: sql_rows_count = 0 end + end + end + + feature {NONE} -- Implementation: User user_salt (a_username: READABLE_STRING_32): detachable READABLE_STRING_8 @@ -743,7 +819,7 @@ feature {NONE} -- Sql Queries: USER ACTIVATION Sql_remove_activation: STRING = "DELETE FROM users_activations WHERE token = :token;" -- Remove activation token. -feature {NONE} +feature {NONE} -- User Password Recovery sql_insert_password: STRING = "INSERT INTO users_password_recovery (token, uid, created) VALUES (:token, :uid, :utc_date);" -- SQL insert a new password recovery :token. @@ -754,6 +830,14 @@ feature {NONE} Select_user_by_password_token: STRING = "SELECT u.* FROM users as u JOIN users_password_recovery as ua ON ua.uid = u.uid and ua.token = :token;" -- Retrieve user by password token if exist. +feature {NONE}-- User Oauth2 Gmail. + Sql_insert_oauth2_gmail: STRING = "INSERT INTO oauth2_gmail (uid, access_token, details, created) VALUES (:uid, :token, :profile, :utc_date);" + + Sql_update_oauth2_gmail: STRING = "UPDATE oauth2_gmail SET access_token = :token, details = :profile WHERE uid =:uid;" + + Select_user_by_oauth2_gmail_token: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.access_token = :token;" + + Select_user_oauth2_gmail_by_id: STRING = "SELECT u.* FROM users as u JOIN oauth2_gmail as og ON og.uid = u.uid and og.uid = :uid;" end diff --git a/src/service/user/cms_user_api.e b/src/service/user/cms_user_api.e index 8465f91..87236e7 100644 --- a/src/service/user/cms_user_api.e +++ b/src/service/user/cms_user_api.e @@ -151,6 +151,35 @@ feature -- Change User storage.update_user (a_user) end + + new_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Add a new user with oauth2 gmail authentication. + require + has_id: a_user.has_id + do + storage.new_user_oauth2_gmail (a_token, a_user_profile, a_user) + end + + + update_user_oauth2_gmail (a_token: READABLE_STRING_32; a_user_profile: READABLE_STRING_32; a_user: CMS_USER) + -- Updaate user `a_user' with oauth2 gmail authentication. + require + has_id: a_user.has_id + do + storage.update_user_oauth2_gmail (a_token, a_user_profile, a_user) + end + + + user_oauth2_gmail_by_id (a_uid: like {CMS_USER}.id): detachable CMS_USER + do + Result := storage.user_oauth2_gmail_by_id (a_uid) + end + + user_by_oauth2_gmail_token (a_token: READABLE_STRING_32): detachable CMS_USER + do + Result := storage.user_by_oauth2_gmail_token (a_token) + end + feature -- User Activation new_activation (a_token: READABLE_STRING_32; a_id: INTEGER_64)