From 2a01f470a02fb0ef1683a5eb6ab5bfa0308b31fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Ritzl?= Date: Sun, 23 Aug 2020 21:00:12 +0200 Subject: [PATCH 1/6] Cleaned up exension and simplified it a lot --- game.project | 4 +- main/main.gui | 179 +++++------- main/main.gui_script | 30 +- siwa/src/siwa.cpp | 312 +++++++++++++++++++++ siwa/src/siwa.h | 93 +++++++ siwa/src/siwa_callbacks.cpp | 531 ------------------------------------ siwa/src/siwa_callbacks.h | 104 ------- siwa/src/siwa_ios.h | 33 --- siwa/src/siwa_ios.mm | 270 ++++-------------- siwa/src/siwa_main.cpp | 139 ---------- 10 files changed, 531 insertions(+), 1164 deletions(-) create mode 100755 siwa/src/siwa.cpp create mode 100755 siwa/src/siwa.h delete mode 100755 siwa/src/siwa_callbacks.cpp delete mode 100755 siwa/src/siwa_callbacks.h delete mode 100755 siwa/src/siwa_ios.h delete mode 100755 siwa/src/siwa_main.cpp diff --git a/game.project b/game.project index 84a1974..fe0d1d3 100755 --- a/game.project +++ b/game.project @@ -13,8 +13,8 @@ main_collection = /main/main.collectionc [display] width = 640 -height = 480 +height = 1136 [ios] -bundle_identifier = com.defold.siwa +bundle_identifier = com.defold.extension.siwa diff --git a/main/main.gui b/main/main.gui index 33288b6..6aa1014 100755 --- a/main/main.gui +++ b/main/main.gui @@ -9,123 +9,6 @@ background_color { z: 0.0 w: 0.0 } -nodes { - position { - x: 320.0 - y: 239.0 - z: 0.0 - w: 1.0 - } - rotation { - x: 0.0 - y: 0.0 - z: 0.0 - w: 1.0 - } - scale { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - size { - x: 640.0 - y: 480.0 - z: 0.0 - w: 1.0 - } - color { - x: 0.0 - y: 0.0 - z: 0.0 - w: 1.0 - } - type: TYPE_BOX - blend_mode: BLEND_MODE_ALPHA - texture: "" - id: "box" - xanchor: XANCHOR_NONE - yanchor: YANCHOR_NONE - pivot: PIVOT_CENTER - adjust_mode: ADJUST_MODE_FIT - layer: "" - inherit_alpha: true - slice9 { - x: 0.0 - y: 0.0 - z: 0.0 - w: 0.0 - } - clipping_mode: CLIPPING_MODE_NONE - clipping_visible: true - clipping_inverted: false - alpha: 1.0 - template_node_child: false - size_mode: SIZE_MODE_MANUAL -} -nodes { - position { - x: 3.0 - y: 2.0 - z: 0.0 - w: 1.0 - } - rotation { - x: 0.0 - y: 0.0 - z: 0.0 - w: 1.0 - } - scale { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - size { - x: 200.0 - y: 100.0 - z: 0.0 - w: 1.0 - } - color { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - type: TYPE_TEXT - blend_mode: BLEND_MODE_ALPHA - text: "Hello World, I\'m SIWA" - font: "system_font" - id: "test_text" - xanchor: XANCHOR_NONE - yanchor: YANCHOR_NONE - pivot: PIVOT_CENTER - outline { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - shadow { - x: 1.0 - y: 1.0 - z: 1.0 - w: 1.0 - } - adjust_mode: ADJUST_MODE_FIT - line_break: false - parent: "box" - layer: "" - inherit_alpha: true - alpha: 1.0 - outline_alpha: 1.0 - shadow_alpha: 1.0 - template_node_child: false - text_leading: 1.0 - text_tracking: 0.0 -} nodes { position { x: 114.0 @@ -601,6 +484,68 @@ nodes { text_leading: 1.0 text_tracking: 0.0 } +nodes { + position { + x: 20.0 + y: 899.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Hello World, I\'m SIWA" + font: "system_font" + id: "test_text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_NW + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: true + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 1.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} material: "/builtins/materials/gui.material" adjust_reference: ADJUST_REFERENCE_PARENT max_nodes: 512 diff --git a/main/main.gui_script b/main/main.gui_script index 1a660d9..b2c083f 100755 --- a/main/main.gui_script +++ b/main/main.gui_script @@ -1,21 +1,20 @@ local dl = require("dirtylarry.dirtylarry") -local good_id = "001869.37272b1d2d6e454d8b396e521ba4fc12.1006" -local bad_id = "abc123" - +local lines = {} local function log(msg, ...) msg = msg:format(...) print(msg) - gui.set_text(gui.get_node("test_text"), msg) + + table.insert(lines, 1, msg) + table.remove(lines, 10) + gui.set_text(gui.get_node("test_text"), table.concat(lines, "\n")) end local function check_credentials_status(self, id) log("check_credentials_status %s", id) siwa.check_credentials_status(id, function(self, data) - log("result: %s", data.result) - for k, v in pairs(data) do - pprint(k) - pprint(v) + for k,v in pairs(data) do + log("%s: %s", k, tostring(v)) end end) end @@ -23,10 +22,9 @@ end local function authenticate(self) log("authenticate") siwa.authenticate(function(self, data) - log("result: %s", data.result) - for k, v in pairs(data) do - print(k) - pprint(v) + self.user_id = data.user_id + for k,v in pairs(data) do + log("%s: %s", k, tostring(v)) end end) end @@ -48,11 +46,15 @@ end function on_input(self, action_id, action) if is_siwa_supported() then dl:button("check", action_id, action, function() - check_credentials_status(self, good_id) + if self.user_id then + check_credentials_status(self, self.user_id) + else + log("No user id. Login first") + end end) dl:button("check_fail", action_id, action, function() - check_credentials_status(self, bad_id) + check_credentials_status(self, "foobar") end) dl:button("login", action_id, action, function() diff --git a/siwa/src/siwa.cpp b/siwa/src/siwa.cpp new file mode 100755 index 0000000..7dd47fd --- /dev/null +++ b/siwa/src/siwa.cpp @@ -0,0 +1,312 @@ +#if defined(DM_PLATFORM_IOS) + +#include "siwa.h" +#include + + +#define MODULE_NAME "siwa" + +SiwaData g_SiwaData; +SiwaCallbackData g_SiwaCallbackData; + + +char* SiwaGetUserId() +{ + return g_SiwaData.m_userID; +} + +void SiwaResetCallbackData() +{ + free(g_SiwaCallbackData.m_userID); + g_SiwaCallbackData.m_userID = 0; + free(g_SiwaCallbackData.m_identityToken); + g_SiwaCallbackData.m_identityToken = 0; + free(g_SiwaCallbackData.m_userID); + g_SiwaCallbackData.m_userID = 0; + free(g_SiwaCallbackData.m_email); + g_SiwaCallbackData.m_email = 0; + free(g_SiwaCallbackData.m_firstName); + g_SiwaCallbackData.m_firstName = 0; + free(g_SiwaCallbackData.m_familyName); + g_SiwaCallbackData.m_familyName = 0; + free(g_SiwaCallbackData.m_identityToken); + g_SiwaCallbackData.m_identityToken = 0; + free(g_SiwaCallbackData.m_message); + g_SiwaCallbackData.m_message = 0; + + g_SiwaCallbackData.m_userStatus = -1; + g_SiwaCallbackData.m_state = STATE_UNKNOWN; + g_SiwaCallbackData.m_cmd = CMD_NONE; +} + +void SiwaQueueCredentialCallback(char* userID, SiwaCredentialState state) +{ + if(g_SiwaCallbackData.m_cmd != CMD_NONE) { + dmLogError("Can't queue credential callback, already have a callback queued!"); + return; + } + + g_SiwaCallbackData.m_cmd = CMD_CREDENTIAL; + g_SiwaCallbackData.m_userID = strdup(userID); + g_SiwaCallbackData.m_state = state; +} + + +void SiwaQueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus) +{ + if(g_SiwaCallbackData.m_cmd != CMD_NONE) { + dmLogError("Can't queue auth success callback, already have a callback queued!"); + return; + } + + g_SiwaCallbackData.m_cmd = CMD_AUTH_SUCCESS; + g_SiwaCallbackData.m_identityToken = strdup(identityToken); + g_SiwaCallbackData.m_userID = strdup(userID); + g_SiwaCallbackData.m_email = strdup(email != 0 ? email: ""); + g_SiwaCallbackData.m_firstName = strdup(firstName != 0 ? firstName: ""); + g_SiwaCallbackData.m_familyName = strdup(familyName != 0 ? familyName : ""); + g_SiwaCallbackData.m_userStatus = userStatus; + g_SiwaCallbackData.m_message = strdup(""); +} + +void SiwaQueueAuthFailureCallback(const char* message) +{ + if(g_SiwaCallbackData.m_cmd != CMD_NONE) { + dmLogError("Can't queue auth error callback, already have a callback queued!"); + return; + } + + g_SiwaCallbackData.m_cmd = CMD_AUTH_FAILED; + g_SiwaCallbackData.m_message = strdup(message); +} + + +void SiwaRunCallback() +{ + lua_State* L = dmScript::GetCallbackLuaContext(g_SiwaData.m_callback); + DM_LUA_STACK_CHECK(L, 0); + + if (dmScript::SetupCallback(g_SiwaData.m_callback)) + { + lua_createtable(L, 0, 3); + + if (g_SiwaCallbackData.m_cmd == CMD_CREDENTIAL) + { + lua_pushstring(L, "result"); + lua_pushstring(L, "SUCCESS"); + lua_settable(L, -3); + + lua_pushstring(L, "user_id"); + lua_pushstring(L, g_SiwaCallbackData.m_userID); + lua_settable(L, -3); + + lua_pushstring(L, "credentials_state"); + lua_pushnumber(L, g_SiwaCallbackData.m_state); + lua_settable(L, -3); + } + else if (g_SiwaCallbackData.m_cmd == CMD_AUTH_SUCCESS) + { + lua_pushstring(L, "result"); + lua_pushstring(L, "SUCCESS"); + lua_settable(L, -3); + + lua_pushstring(L, "identity_token"); + lua_pushstring(L, g_SiwaCallbackData.m_identityToken); + lua_settable(L, -3); + + lua_pushstring(L, "user_id"); + lua_pushstring(L, g_SiwaCallbackData.m_userID); + lua_settable(L, -3); + + lua_pushstring(L, "email"); + lua_pushstring(L, g_SiwaCallbackData.m_email); + lua_settable(L, -3); + + lua_pushstring(L, "first_name"); + lua_pushstring(L, g_SiwaCallbackData.m_firstName); + lua_settable(L, -3); + + lua_pushstring(L, "family_name"); + lua_pushstring(L, g_SiwaCallbackData.m_familyName); + lua_settable(L, -3); + + lua_pushstring(L, "user_status"); + lua_pushnumber(L, g_SiwaCallbackData.m_userStatus); + lua_settable(L, -3); + } + else if (g_SiwaCallbackData.m_cmd == CMD_AUTH_FAILED) + { + lua_pushstring(L, "result"); + lua_pushstring(L, "ERROR"); + lua_settable(L, -3); + + lua_pushstring(L, "message"); + lua_pushstring(L, g_SiwaCallbackData.m_message); + lua_settable(L, -3); + } + + if (lua_pcall(L, 2, 0, 0) != 0) + { + dmLogError("Error running siwa callback: %s", lua_tostring(L, -1)); + lua_pop(L, 1); + } + dmScript::TeardownCallback(g_SiwaData.m_callback); + } +} + + +void SiwaSetupCallback(lua_State* L, int index) +{ + if (g_SiwaData.m_callback) { + dmScript::DestroyCallback(g_SiwaData.m_callback); + } + g_SiwaData.m_callback = dmScript::CreateCallback(L, index); +} + +void SiwaCleanupCallback() { + if (g_SiwaData.m_callback) { + dmScript::DestroyCallback(g_SiwaData.m_callback); + g_SiwaData.m_callback = 0; + } +} + + +// Function for asking Apple if a provided user id currently grants us access to it for this app. +// Expects: (a string containing the user id, and a callback that expects a self instance and a table containing results). +// The callback will be triggered at some point later after getting a response from Apple. +// The response will be SUCCESS whether or not the id granted us permission. +// It will give us a status of 1 if the user is authorized, and 2 if it's not. +int SiwaCheckStatusOfAppleID(lua_State* L){ + DM_LUA_STACK_CHECK(L, 1); + + if (!SiwaIsAvailable()) { + dmLogWarning("Sign in with Apple is not available"); + lua_pushboolean(L, 0); + return 1; + } + + if(g_SiwaData.m_callback != 0) + { + dmLogError("Callback already in progress"); + lua_pushboolean(L, 0); + return 1; + } + + int num_args = lua_gettop(L); + if(num_args != 2) { + dmLogError("Incorrect number of args passed to SiwaCheckStatusOfAppleID!"); + lua_pushboolean(L, 0); + return 1; + } + + luaL_checktype(L, 1, LUA_TSTRING); + if (g_SiwaData.m_userID) free(g_SiwaData.m_userID); + g_SiwaData.m_userID = strdup(lua_tostring(L, 1)); + + luaL_checktype(L, 2, LUA_TFUNCTION); + SiwaSetupCallback(L, 2); + SiwaCheckStatusOfAppleID(); + + lua_pushboolean(L, 1); + return 1; +} + +// Function for having a user sign in with Apple. +// Expects: (a callback that expects a self instance and a table containing results). +// The callback will be triggered at some point later after getting a response from Apple. +// The first time a user signs in after granting permission to this app, the callback will give us a name and email +// as well as a user id and identity token on success. +// On subsequent logins, we will only get the user id and identity token. +// On a failure, such as when the user cancels the login flow, we will get ERROR as a result, and a message +// describing the error. +int SiwaAuthenticateWithApple(lua_State* L) { + DM_LUA_STACK_CHECK(L, 1); + + if (!SiwaIsAvailable()) { + dmLogWarning("Sign in with Apple is not available"); + lua_pushboolean(L, 0); + return 1; + } + + if(g_SiwaData.m_callback != 0) + { + dmLogError("Callback already in progress"); + lua_pushboolean(L, 0); + return 1; + } + + luaL_checktype(L, 1, LUA_TFUNCTION); + SiwaSetupCallback(L, 1); + SiwaAuthenticateWithApple(); + + lua_pushboolean(L, 1); + return 1; +} + +int SiwaIsSupported(lua_State* L) { + DM_LUA_STACK_CHECK(L, 1); + lua_pushboolean(L, SiwaIsAvailable()); + return 1; +} + + +static dmExtension::Result SiwaAppInitialize(dmExtension::AppParams* params) +{ + SiwaResetCallbackData(); + return dmExtension::RESULT_OK; +} + +static dmExtension::Result SiwaAppFinalize(dmExtension::AppParams* params) +{ + return dmExtension::RESULT_OK; +} + +const luaL_reg lua_register[] = +{ + {"is_siwa_supported", SiwaIsSupported}, + {"check_credentials_status", SiwaCheckStatusOfAppleID}, + {"authenticate", SiwaAuthenticateWithApple}, + {0, 0} +}; + +static dmExtension::Result SiwaInitialize(dmExtension::Params* params) +{ + lua_State* L = params->m_L; + int top = lua_gettop(L); + luaL_register(L, MODULE_NAME, lua_register); + + #define SETCONSTANT(name) \ + lua_pushnumber(L, (lua_Number) name); \ + lua_setfield(L, -2, #name);\ + + SETCONSTANT(STATE_NOT_FOUND) + SETCONSTANT(STATE_UNKNOWN) + SETCONSTANT(STATE_AUTHORIZED) + SETCONSTANT(STATE_REVOKED) + + #undef SETCONSTANT + + lua_pop(L, 1); + assert(top == lua_gettop(L)); + return dmExtension::RESULT_OK; +} + +static dmExtension::Result SiwaUpdate(dmExtension::Params* params) +{ + if(g_SiwaCallbackData.m_cmd != CMD_NONE) + { + SiwaRunCallback(); + SiwaResetCallbackData(); + SiwaCleanupCallback(); + } + return dmExtension::RESULT_OK; +} + +static dmExtension::Result SiwaFinalize(dmExtension::Params* params) +{ + return dmExtension::RESULT_OK; +} + +DM_DECLARE_EXTENSION(siwa, MODULE_NAME, SiwaAppInitialize, SiwaAppFinalize, SiwaInitialize, SiwaUpdate, 0, SiwaFinalize); + +#endif diff --git a/siwa/src/siwa.h b/siwa/src/siwa.h new file mode 100755 index 0000000..00284f1 --- /dev/null +++ b/siwa/src/siwa.h @@ -0,0 +1,93 @@ +#pragma once +#if defined(DM_PLATFORM_IOS) + +#include + +enum SiwaCallbackCmd +{ + CMD_NONE = 0, + CMD_CREDENTIAL = 1, + CMD_AUTH_SUCCESS = 2, + CMD_AUTH_FAILED = 3 +}; + +enum SiwaCredentialState +{ + STATE_UNKNOWN = 0, + STATE_AUTHORIZED = 1, + STATE_REVOKED = 2, + STATE_NOT_FOUND = 3 +}; + +struct SiwaCallbackData +{ + SiwaCallbackData() + { + memset(this, 0, sizeof(*this)); + }; + + SiwaCallbackCmd m_cmd; + SiwaCredentialState m_state; + + char* m_identityToken; + char* m_userID; + char* m_email; + char* m_firstName; + char* m_familyName; + int m_userStatus; + + char* m_message; +}; + +// Data used to ensure we only have 1 request in progress with Apples servers at a time. +// Access should be restricted behind mutex protection as the callbacks from apple can +// be triggered in arbitrary contexts. +// This data is also used to hold information passed from lua until the callback comes +// back from Apple, so that we may pass it back to lua to identify the request we are +// getting a callback for. +struct SiwaData +{ + SiwaData() + { + memset(this, 0, sizeof(*this)); + }; + + // the user ID used for checking credential state + char* m_userID; + + dmScript::LuaCallbackInfo* m_callback; +}; + +char* SiwaGetUserId(); + +void SiwaCheckForQueuedCallbacks(); +void SiwaInitCallbackData(); +void SiwaResetCallbackData(); + +// Queue the credential check callback to be triggered next update call in the main thread. +void SiwaQueueCredentialCallback(char* userID, SiwaCredentialState state); +// Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization succeeds. +void SiwaQueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus); +// Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization fails. +void SiwaQueueAuthFailureCallback(const char* message); + +void SiwaRunCallback(); + +void SiwaSetupCallback(lua_State* L, int index); + +void SiwaCleanupCallback(); + +// Trigged by a call from lua to check if sign in with apple is supported on this device. +bool SiwaIsAvailable(); + +// Triggered by a call from lua to start the sign in with apple flow +// expects the callback to be a reference number to the lua registry +// expects the context to be reference number to the lua registry +void SiwaAuthenticateWithApple(); + +// Triggered by a call from lua to check if a provided apple id grants this app permission to use that id. +// expects the callback to be a reference number to the lua registry +// expects the context to be reference number to the lua registry +void SiwaCheckStatusOfAppleID(); + +#endif diff --git a/siwa/src/siwa_callbacks.cpp b/siwa/src/siwa_callbacks.cpp deleted file mode 100755 index 094e100..0000000 --- a/siwa/src/siwa_callbacks.cpp +++ /dev/null @@ -1,531 +0,0 @@ -#include "siwa_callbacks.h" - -#if defined(DM_PLATFORM_IOS) - -#include -#include -#include - -#include -#include - -namespace dmSiwa { - - // access to these structs should be protected by mutex - // as they are used by callbacks that can trigger in arbitrary contexts. - static CredentialCallbackData g_CredentialCallbackData; - static AuthCallbackData g_AuthCallbackData; - - static dmMutex::HMutex g_CredentialCallbackMutex; - static dmMutex::HMutex g_AuthCallbackMutex; -} - -void dmSiwa::InitCallbacks() -{ - InitCredentialCallbackData(); - InitAuthCallbackData(); -} - -void dmSiwa::CheckForQueuedCallbacks() -{ - CheckForCredentialCallback(); - CheckForAuthCallback(); -} - -// do the common setup for lua callbacks for this extension -bool dmSiwa::RunCallbackSetup(lua_State* L, int _self, int _callback) -{ - if (_callback != LUA_NOREF) - { - lua_rawgeti(L, LUA_REGISTRYINDEX, _callback); - lua_rawgeti(L, LUA_REGISTRYINDEX, _self); - lua_pushvalue(L, -1); - dmScript::SetInstance(L); - return true; - } - else - { - dmLogError("No callback set for siwa"); - return false; - } -} - -//================================================================== -// Callback for checking the credential state of a provided sign in with Apple ID -//================================================================== -void dmSiwa::InitCredentialCallbackData() -{ - g_CredentialCallbackMutex = dmMutex::New(); - ResetCredentialCallbackData(); -} - -void dmSiwa::ResetCredentialCallbackData() -{ - DM_MUTEX_SCOPED_LOCK(g_CredentialCallbackMutex) - g_CredentialCallbackData.m_L = nullptr; - g_CredentialCallbackData.m_self = LUA_NOREF; - g_CredentialCallbackData.m_callback = LUA_NOREF; - g_CredentialCallbackData.m_userID = nullptr; - g_CredentialCallbackData.m_status = -1; - - g_CredentialCallbackData.m_cbState = IDLE; -} - -void dmSiwa::QueueCredentialCallback(lua_State* L, int _self, int _callback, char* _userID, int status) -{ - DM_MUTEX_SCOPED_LOCK(g_CredentialCallbackMutex) - if(g_CredentialCallbackData.m_cbState != IDLE){ - dmLogError("Can't queue credential callback, already queued or running!"); - return; - } - - g_CredentialCallbackData.m_L = L; - g_CredentialCallbackData.m_self = _self; - g_CredentialCallbackData.m_callback = _callback; - g_CredentialCallbackData.m_userID = strdup(_userID); - g_CredentialCallbackData.m_status = status; - - g_CredentialCallbackData.m_cbState = QUEUED; -} - -void dmSiwa::RunCredentialCallback() -{ - lua_State* L = nullptr; - int _self = LUA_NOREF; - int _callback = LUA_NOREF; - char* _userID = nullptr; - int status = -1; - - { - DM_MUTEX_SCOPED_LOCK(g_CredentialCallbackMutex) - - L = g_CredentialCallbackData.m_L; - - _self = g_CredentialCallbackData.m_self; - g_CredentialCallbackData.m_self = LUA_NOREF; - - _callback = g_CredentialCallbackData.m_callback; - g_CredentialCallbackData.m_callback = LUA_NOREF; - - if(g_CredentialCallbackData.m_userID != nullptr) - { - _userID = strdup(g_CredentialCallbackData.m_userID); - free(g_CredentialCallbackData.m_userID); - g_CredentialCallbackData.m_userID = nullptr; - } - - status = g_CredentialCallbackData.m_status; - } - - // if the pre setup fails, bail - if(L == nullptr or _self == LUA_NOREF or _callback == LUA_NOREF or _userID == nullptr - or !RunCallbackSetup(L, _self, _callback)) - { - dmLogError("Could not run siwa credential callback because a parameter was null or missing"); - lua_pop(L, 2); - return; - } - - if (dmScript::IsInstanceValid(L)) - { - lua_createtable(L, 0, 3); - - lua_pushstring(L, "result"); - lua_pushstring(L, "SUCCESS"); - lua_settable(L, -3); - - lua_pushstring(L, "user_id"); - lua_pushstring(L, _userID); - lua_settable(L, -3); - - lua_pushstring(L, "credential_status"); - lua_pushnumber(L, status); - lua_settable(L, -3); - - if (lua_pcall(L, 2, 0, 0) != 0) - { - dmLogError("Error running siwa credential callback: %s", lua_tostring(L, -1)); - lua_pop(L, 1); - } - - free(_userID); - dmScript::Unref(L, LUA_REGISTRYINDEX, _callback); - dmScript::Unref(L, LUA_REGISTRYINDEX, _self); - } - else - { - dmLogError("Could not run siwa credential callback because the instance is invalid."); - lua_pop(L, 2); - } -} - -void dmSiwa::CheckForCredentialCallback() -{ - bool triggerCallback = false; - { - DM_MUTEX_SCOPED_LOCK(g_CredentialCallbackMutex) - if(g_CredentialCallbackData.m_cbState == QUEUED) - { - triggerCallback = true; - g_CredentialCallbackData.m_cbState = RUNNING; - } - } - - if(triggerCallback) - { - RunCredentialCallback(); - ResetCredentialCallbackData(); - } -} - -//================================================================== -// Callback for the sign in with Apple ID -//================================================================== - -void dmSiwa::InitAuthCallbackData() -{ - g_AuthCallbackMutex = dmMutex::New(); - ResetAuthCallbackData(); -} - -void dmSiwa::ResetAuthCallbackData() -{ - DM_MUTEX_SCOPED_LOCK(g_AuthCallbackMutex) - g_AuthCallbackData.m_L = nullptr; - g_AuthCallbackData.m_self = LUA_NOREF; - g_AuthCallbackData.m_callback = LUA_NOREF; - - g_AuthCallbackData.m_identityToken = nullptr; - g_AuthCallbackData.m_userID = nullptr; - g_AuthCallbackData.m_email = nullptr; - g_AuthCallbackData.m_firstName = nullptr; - g_AuthCallbackData.m_familyName = nullptr; - - g_AuthCallbackData.m_userStatus = -1; - - g_AuthCallbackData.m_message = nullptr; - - g_AuthCallbackData.m_cbState = IDLE; - g_AuthCallbackData.m_success = false; -} - -void dmSiwa::QueueAuthSuccessCallback(lua_State* L, int _self, int _callback, const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus) -{ - DM_MUTEX_SCOPED_LOCK(g_AuthCallbackMutex) - if(g_AuthCallbackData.m_cbState != IDLE){ - dmLogError("Can't queue auth success callback, already queued or running!"); - return; - } - - g_AuthCallbackData.m_L = L; - g_AuthCallbackData.m_self = _self; - g_AuthCallbackData.m_callback = _callback; - - if(identityToken != nullptr) - { - g_AuthCallbackData.m_identityToken = strdup(identityToken); - } - - if(userID != nullptr) - { - g_AuthCallbackData.m_userID = strdup(userID); - } - - if(email != nullptr) - { - g_AuthCallbackData.m_email = strdup(email); - } - - if(firstName != nullptr) - { - g_AuthCallbackData.m_firstName = strdup(firstName); - } - - if(familyName != nullptr) - { - g_AuthCallbackData.m_familyName = strdup(familyName); - } - - g_AuthCallbackData.m_userStatus = userStatus; - - g_AuthCallbackData.m_message = nullptr; - - g_AuthCallbackData.m_cbState = QUEUED; - g_AuthCallbackData.m_success = true; -} - -void dmSiwa::QueueAuthFailureCallback(lua_State* L, int _self, int _callback, const char* message) -{ - DM_MUTEX_SCOPED_LOCK(g_AuthCallbackMutex) - if(g_AuthCallbackData.m_cbState != IDLE){ - dmLogError("Can't queue auth error callback, already queued or running!"); - return; - } - - g_AuthCallbackData.m_L = L; - g_AuthCallbackData.m_self = _self; - g_AuthCallbackData.m_callback = _callback; - - g_AuthCallbackData.m_message = strdup(message); - - g_AuthCallbackData.m_cbState = QUEUED; - g_AuthCallbackData.m_success = false; -} - -void dmSiwa::CheckForAuthCallback() -{ - bool triggerCallback = false; - bool success = false; - { - DM_MUTEX_SCOPED_LOCK(g_AuthCallbackMutex) - if(g_AuthCallbackData.m_cbState == QUEUED) - { - triggerCallback = true; - success = g_AuthCallbackData.m_success; - g_AuthCallbackData.m_cbState = RUNNING; - } - } - - if(triggerCallback) - { - if(success) - { - RunAuthSuccessCallback(); - } - else - { - RunAuthFailureCallback(); - } - - ResetAuthCallbackData(); - } -} - -void dmSiwa::RunAuthSuccessCallback() -{ - lua_State* L = nullptr; - int _self = LUA_NOREF; - int _callback = LUA_NOREF; - - char* identityToken = nullptr; - char* userID = nullptr; - char* email = nullptr; - char* firstName = nullptr; - char* familyName = nullptr; - int userStatus = -1; - { - DM_MUTEX_SCOPED_LOCK(g_AuthCallbackMutex); - L = g_AuthCallbackData.m_L; - userStatus = g_AuthCallbackData.m_userStatus; - g_AuthCallbackData.m_L = nullptr; - - // take ownership of auth data - _self = g_AuthCallbackData.m_self; - g_AuthCallbackData.m_self = LUA_NOREF; - - _callback = g_AuthCallbackData.m_callback; - g_AuthCallbackData.m_callback = LUA_NOREF; - - if(g_AuthCallbackData.m_identityToken != nullptr) - { - identityToken = strdup(g_AuthCallbackData.m_identityToken); - free(g_AuthCallbackData.m_identityToken); - g_AuthCallbackData.m_identityToken = nullptr; - } - - if(g_AuthCallbackData.m_userID != nullptr) - { - userID = strdup(g_AuthCallbackData.m_userID); - free(g_AuthCallbackData.m_userID); - g_AuthCallbackData.m_userID = nullptr; - } - - if(g_AuthCallbackData.m_email != nullptr) - { - email = strdup(g_AuthCallbackData.m_email); - free(g_AuthCallbackData.m_email); - g_AuthCallbackData.m_email = nullptr; - } - - if(g_AuthCallbackData.m_firstName != nullptr) - { - firstName = strdup(g_AuthCallbackData.m_firstName); - free(g_AuthCallbackData.m_firstName); - g_AuthCallbackData.m_firstName = nullptr; - } - - if(g_AuthCallbackData.m_familyName != nullptr) - { - familyName = strdup(g_AuthCallbackData.m_familyName); - free(g_AuthCallbackData.m_familyName); - g_AuthCallbackData.m_familyName = nullptr; - } - } - - // if the pre setup fails, bail - // first name, family name, and email can be null in normal execution, as they are not provided - // after a user's first login to our app. Userid and identity token should always be provided. - if(L == nullptr or _self == LUA_NOREF or _callback == LUA_NOREF or identityToken == nullptr or userID == nullptr - or !RunCallbackSetup(L, _self, _callback)) - { - dmLogError("Could not run siwa auth success callback because a parameter was null or missing"); - - free(identityToken); - free(userID); - free(email); - free(firstName); - free(familyName); - - if(_self != LUA_NOREF) - { - dmScript::Unref(L, LUA_REGISTRYINDEX, _self); - } - - if(_callback != LUA_NOREF) - { - dmScript::Unref(L, LUA_REGISTRYINDEX, _callback); - } - - lua_pop(L, 2); - return; - } - - if (dmScript::IsInstanceValid(L)) - { - lua_createtable(L, 0, 7); - - lua_pushstring(L, "result"); - lua_pushstring(L, "SUCCESS"); - lua_settable(L, -3); - - lua_pushstring(L, "identity_token"); - lua_pushstring(L, identityToken); - lua_settable(L, -3); - - lua_pushstring(L, "user_id"); - lua_pushstring(L, userID); - lua_settable(L, -3); - - lua_pushstring(L, "email"); - lua_pushstring(L, email); - lua_settable(L, -3); - - lua_pushstring(L, "first_name"); - lua_pushstring(L, firstName); - lua_settable(L, -3); - - lua_pushstring(L, "family_name"); - lua_pushstring(L, familyName); - lua_settable(L, -3); - - lua_pushstring(L, "user_status"); - lua_pushnumber(L, userStatus); - lua_settable(L, -3); - - if (lua_pcall(L, 2, 0, 0) != 0) - { - dmLogError("Error running siwa auth success callback: %s", lua_tostring(L, -1)); - lua_pop(L, 1); - } - - // clean up owned data - free(identityToken); - free(userID); - free(email); - free(firstName); - free(familyName); - - dmScript::Unref(L, LUA_REGISTRYINDEX, _callback); - dmScript::Unref(L, LUA_REGISTRYINDEX, _self); - } - else - { - dmLogError("Could not run siwa auth success callback because the instance is invalid."); - lua_pop(L, 2); - } -} - -void dmSiwa::RunAuthFailureCallback() -{ - lua_State* L = nullptr; - int _self = LUA_NOREF; - int _callback = LUA_NOREF; - - char* message = nullptr; - { - DM_MUTEX_SCOPED_LOCK(g_AuthCallbackMutex) - L = g_AuthCallbackData.m_L; - - // take ownership of authorization data - _self = g_AuthCallbackData.m_self; - g_AuthCallbackData.m_self = LUA_NOREF; - - _callback = g_AuthCallbackData.m_callback; - g_AuthCallbackData.m_callback = LUA_NOREF; - - if(g_AuthCallbackData.m_message != nullptr) - { - message = strdup(g_AuthCallbackData.m_message); - free(g_AuthCallbackData.m_message); - g_AuthCallbackData.m_message = nullptr; - } - } - - // if the pre setup fails, bail - if(L == nullptr or _self == LUA_NOREF or _callback == LUA_NOREF or message == nullptr - or !RunCallbackSetup(L, _self, _callback)) - { - dmLogError("Could not run siwa auth error callback because a parameter was null or missing"); - - free(message); - - if(_self != LUA_NOREF) - { - dmScript::Unref(L, LUA_REGISTRYINDEX, _self); - } - - if(_callback != LUA_NOREF) - { - dmScript::Unref(L, LUA_REGISTRYINDEX, _callback); - } - - lua_pop(L, 2); - return; - } - - if (dmScript::IsInstanceValid(L)) - { - lua_createtable(L, 0, 2); - - lua_pushstring(L, "result"); - lua_pushstring(L, "ERROR"); - lua_settable(L, -3); - - lua_pushstring(L, "message"); - lua_pushstring(L, message); - lua_settable(L, -3); - - if (lua_pcall(L, 2, 0, 0) != 0) - { - dmLogError("Error running siwa auth error callback: %s", lua_tostring(L, -1)); - lua_pop(L, 1); - } - - free(message); - dmScript::Unref(L, LUA_REGISTRYINDEX, _callback); - dmScript::Unref(L, LUA_REGISTRYINDEX, _self); - } - else - { - dmLogError("Could not run siwa auth error callback because the instance is invalid."); - - free(message); - dmScript::Unref(L, LUA_REGISTRYINDEX, _callback); - dmScript::Unref(L, LUA_REGISTRYINDEX, _self); - - // We need to clean up our stack, which has 2 elements at this point (see comment 4:) - lua_pop(L, 2); - } -} - -#endif diff --git a/siwa/src/siwa_callbacks.h b/siwa/src/siwa_callbacks.h deleted file mode 100755 index d8c5bf1..0000000 --- a/siwa/src/siwa_callbacks.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -// A collection of helper structs and functions for running lua callbacks provided -// when the module methods were initially called. The sign in with apple sign in flow -// and credential state checking - -struct lua_State; - -namespace dmSiwa { - - // The state of our own callbacks: - // IDLE: default state, available to trigger a callbak - // QUEUED: We've received a callback from able, and have queued our own associated callback - // to run on the main thread. - // RUNNING: Our callback is currently running on the main thread. - enum CallbackState{ - IDLE, - QUEUED, - RUNNING - }; - - // Holds data recevied from the callback for checking credential state - // so that we can access it on the main thread, since the Apple callback - // can be triggered in any arbitrary thread context. - struct CredentialCallbackData{ - public: - - // Standard data needed to trigger a callback in lua - lua_State* m_L; - int m_self; - int m_callback; - - // Data to be passed to lua - char* m_userID; - int m_status; - - // used for determining when to trigger a callback - CallbackState m_cbState; - }; - - // Holds data recevied from the callback for the Apple sign in flow - // so that we can access it on the main thread, since the Apple callback - // can be triggered in any arbitrary thread context. - struct AuthCallbackData{ - public: - - // Standard data needed to trigger a callback in lua - lua_State* m_L; - int m_self; - int m_callback; - - // Data to be passed to lua on a successful authorization - char* m_identityToken; - char* m_userID; - char* m_email; - char* m_firstName; - char* m_familyName; - int m_userStatus; - - // Data to be passed to lua on a failed authorization - char* m_message; - - // used for determining when to trigger a callback - CallbackState m_cbState; - bool m_success; - }; - - // Sets up data and mutexes to handle capturing data from Apple callbacks - // and running our own associated callbacks on the main thread. - void InitCallbacks(); - - // Check if an callbacks have been queued since the last updated on the main - // thread, and if so, trigger the callback. - void CheckForQueuedCallbacks(); - - // Common setup to run each time we want to trigger a callback in lua. - bool RunCallbackSetup(lua_State* L, int _self, int _callback); - - // One-time setup for credential checking callbacks. - void InitCredentialCallbackData(); - // Setup to be run as the credential checking callback is set to idle - void ResetCredentialCallbackData(); - // Queue the credential check callback to be triggered next update call in the main thread. - void QueueCredentialCallback(lua_State* L, int _self, int _callback, char* userID, int status); - // Check if the credential check callback was queued to run in the next update call in the main thread. - void CheckForCredentialCallback(); - // Setup the credential check callback to be called in lua on the main thread, and trigger it - void RunCredentialCallback(); - - // One-time setup for sign in authorization callbacks. - void InitAuthCallbackData(); - // Setup to be run as the sign in authorizationcallback is set to idle - void ResetAuthCallbackData(); - // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization succeeds. - void QueueAuthSuccessCallback(lua_State* L, int _self, int _callback, const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus); - // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization fails. - void QueueAuthFailureCallback(lua_State* L, int _self, int _callback, const char* message); - // Check if the sign in authorization callback was queued to run in the next update call in the main thread. - void CheckForAuthCallback(); - // Setup the sign in authorization callback for when authorization succeeds to be called in lua on the main thread, and trigger it - void RunAuthSuccessCallback(); - // Setup the sign in authorization callback for when authorization fails to be called in lua on the main thread, and trigger it - void RunAuthFailureCallback(); -} diff --git a/siwa/src/siwa_ios.h b/siwa/src/siwa_ios.h deleted file mode 100755 index 3d225c9..0000000 --- a/siwa/src/siwa_ios.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#if defined(DM_PLATFORM_IOS) - -struct lua_State; - -namespace siwa_code { -namespace platform { - - // Standard Defold Native Extension functions - void AppInitialize(); - void AppFinalize(); - void Initialize(); - void Update(); - void Finalize(); - - // Trigged by a call from lua to check if sign in with apple is supported on this device. - int IsSiwaSupported(lua_State* L); - - // Triggered by a call from lua to start the sign in with apple flow - // expects the callback to be a reference number to the lua registry - // expects the context to be reference number to the lua registry - int AuthenticateWithApple(lua_State* L, int callback, int context, lua_State* thread); - - // Triggered by a call from lua to check if a provided apple id grants this app permission to use that id. - // expects the callback to be a reference number to the lua registry - // expects the context to be reference number to the lua registry - int CheckStatusOfAppleID(lua_State* L, char* userID, int callback, int context, lua_State* thread); - - -} // namespace -} // namespace - -#endif diff --git a/siwa/src/siwa_ios.mm b/siwa/src/siwa_ios.mm index 22d33bf..ea5af02 100755 --- a/siwa/src/siwa_ios.mm +++ b/siwa/src/siwa_ios.mm @@ -1,58 +1,14 @@ #if defined(DM_PLATFORM_IOS) -#include "siwa_ios.h" - -#include "siwa_callbacks.h" +#include "siwa.h" #include -#include -#include -#include #include #include #include -// Data used to ensure we only have 1 request in progress with Apples servers at a time. -// Access should be restricted behind mutex protection as the callbacks from apple can -// be triggered in arbitrary contexts. -// This data is also used to hold information passed from lua until the callback comes -// back from Apple, so that we may pass it back to lua to identify the request we are -// getting a callback for. -struct SiwaData -{ - SiwaData() { - memset(this, 0, sizeof(*this)); - m_userID = nullptr; - m_Callback = LUA_NOREF; - m_Self = LUA_NOREF; - - m_waitingOnCallback = false; - } - - // the user ID used for checking credential state - char* m_userID; - - // references the lua state, script instance and function callback - // to be triggered when the request to apple is complete. - int m_Callback; - int m_Self; - lua_State* m_MainThread; - - // used to prevent multiple requests to apple being in progress at once. - bool m_waitingOnCallback; -}; - -static SiwaData g_SiwaData; -static dmMutex::HMutex g_siwaMutex; - -static void CleanupSiwaData() { - g_SiwaData.m_waitingOnCallback = false; - g_SiwaData.m_MainThread = nullptr; - g_SiwaData.m_Self = LUA_NOREF; - g_SiwaData.m_Callback = LUA_NOREF; -} // The sign in with Apple flow expects us to have a delegate to which it can both pass data from the sign in flow // but also how to figure out in which UI context it should display the native login UI. @@ -83,19 +39,8 @@ - (instancetype) init // so we should treat both revoked and unknown as unauthorized. - (void) checkCredentialStatus { - char* user_id = nullptr; - { - DM_MUTEX_SCOPED_LOCK(g_siwaMutex) - user_id = g_SiwaData.m_userID; - } - - if(user_id == nullptr) - { - dmLogInfo("provided user id was NULL!"); - return; - } - - NSString* user_id_string = [[NSString alloc] initWithUTF8String:user_id]; + char* userId = SiwaGetUserId(); + NSString* user_id_string = [[NSString alloc] initWithUTF8String:userId]; [self.m_idProvider getCredentialStateForUserID: user_id_string completion: ^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError* error) { @@ -106,28 +51,26 @@ - (void) checkCredentialStatus dmLogError("getCredentialStateForUserID completed with error"); } + SiwaCredentialState state = STATE_UNKNOWN; switch(credentialState) { case ASAuthorizationAppleIDProviderCredentialAuthorized: - dmLogInfo("credential state: ASAuthorizationAppleIDProviderCredentialAuthorized"); + dmLogInfo("credential state: ASAuthorizationAppleIDProviderCredentialAuthorized"); + state = STATE_AUTHORIZED; break; case ASAuthorizationAppleIDProviderCredentialRevoked: dmLogInfo("credential state: ASAuthorizationAppleIDProviderCredentialRevoked"); + state = STATE_REVOKED; break; case ASAuthorizationAppleIDProviderCredentialNotFound: dmLogInfo("credential state: ASAuthorizationAppleIDProviderCredentialNotFound"); + state = STATE_NOT_FOUND; break; default: dmLogInfo("credential state: unknown!!!"); break; } - DM_MUTEX_SCOPED_LOCK(g_siwaMutex) - dmSiwa::QueueCredentialCallback(g_SiwaData.m_MainThread, g_SiwaData.m_Self, g_SiwaData.m_Callback, g_SiwaData.m_userID, credentialState); - - // now that the callback is queued, we can clean up the held data. - free(g_SiwaData.m_userID); - g_SiwaData.m_userID = nullptr; - CleanupSiwaData(); + SiwaQueueCredentialCallback(userId, state); }]; } @@ -157,9 +100,6 @@ - (void)authorizationController:(ASAuthorizationController *)controller if ([authorization.credential class] == [ASAuthorizationAppleIDCredential class]) { ASAuthorizationAppleIDCredential* appleIdCredential = ((ASAuthorizationAppleIDCredential*) authorization.credential); - /////////////////////////////////////////////// - // Pull data out of the auth and translate to cpp types - /////////////////////////////////////////////// const char* appleUserId = [appleIdCredential.user UTF8String]; const char* email = [appleIdCredential.email UTF8String]; const char* givenName = [appleIdCredential.fullName.givenName UTF8String]; @@ -167,184 +107,66 @@ - (void)authorizationController:(ASAuthorizationController *)controller const int userDetectionStatus = (int) appleIdCredential.realUserStatus; NSString* tokenString = [[NSString alloc] initWithData:appleIdCredential.identityToken encoding:NSUTF8StringEncoding]; const char* identityToken = [tokenString UTF8String]; - /////////////////////////////////////////////// - DM_MUTEX_SCOPED_LOCK(g_siwaMutex) - dmSiwa::QueueAuthSuccessCallback(g_SiwaData.m_MainThread, g_SiwaData.m_Self, g_SiwaData.m_Callback, identityToken, appleUserId, email, givenName, familyName, userDetectionStatus); - CleanupSiwaData(); + SiwaQueueAuthSuccessCallback(identityToken, appleUserId, email, givenName, familyName, userDetectionStatus); } else { - DM_MUTEX_SCOPED_LOCK(g_siwaMutex) - dmSiwa::QueueAuthFailureCallback(g_SiwaData.m_MainThread, g_SiwaData.m_Self, g_SiwaData.m_Callback, "authorization failed!"); - CleanupSiwaData(); + SiwaQueueAuthFailureCallback("authorization failed!"); } } // The Auth controller callback for getting an error during authorization - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error { - DM_MUTEX_SCOPED_LOCK(g_siwaMutex) - dmSiwa::QueueAuthFailureCallback(g_SiwaData.m_MainThread, g_SiwaData.m_Self, g_SiwaData.m_Callback, "authorization error!"); - CleanupSiwaData(); + SiwaQueueAuthFailureCallback("authorization error!"); } @end -namespace siwa_code { -namespace platform { - - void AppInitialize() { - g_siwaMutex = dmMutex::New(); - dmSiwa::InitCallbacks(); - } - void AppFinalize() { - } - - void Initialize() { - } - - void Update() { - dmSiwa::CheckForQueuedCallbacks(); - } - - void Finalize() { - } - - // Checks if Siwa is supported on this device by seeing if the main - // class involved in all the siwa requests we use exists. - // Used by the native code side of this module. - static bool IsSiwaAvailable() - { - return ([ASAuthorizationAppleIDProvider class] != nil); - } - - // Checks if Siwa is supported on this device. - // Exposed to lua. - int IsSiwaSupported(lua_State* L) - { - DM_LUA_STACK_CHECK(L, 1); - - if(IsSiwaAvailable()) - { - lua_pushboolean(L, 1); - } - else - { - lua_pushboolean(L, 0); - } - - return 1; - } - - //Cleans up callback data so that its fresh for the next callback. - //!!! Not Thread safe on g_siwaData !!! - //Make sure whatever is calling this wraps a mutex around this call - static void CallbackSetup(lua_State* L) - { - if (g_SiwaData.m_Callback != LUA_NOREF) { - dmLogError("Unexpected callback set"); - - dmScript::Unref(L, LUA_REGISTRYINDEX, g_SiwaData.m_Callback); - dmScript::Unref(L, LUA_REGISTRYINDEX, g_SiwaData.m_Self); - g_SiwaData.m_Callback = LUA_NOREF; - g_SiwaData.m_Self = LUA_NOREF; - } - - if(g_SiwaData.m_userID != nullptr){ - free(g_SiwaData.m_userID); - g_SiwaData.m_userID = nullptr; - } - } +// Checks if Siwa is supported on this device by seeing if the main +// class involved in all the siwa requests we use exists. +bool SiwaIsAvailable() +{ + return ([ASAuthorizationAppleIDProvider class] != nil); +} - API_AVAILABLE(ios(13.0)) - static SiwaManager* g_siwaManager = nil; +API_AVAILABLE(ios(13.0)) +static SiwaManager* g_SiwaManager = nil; - API_AVAILABLE(ios(13.0)) - static SiwaManager* GetSiwaManager() +API_AVAILABLE(ios(13.0)) +SiwaManager* GetSiwaManager() +{ + if(g_SiwaManager == nil) { - if(g_siwaManager == nil) - { - g_siwaManager = [[SiwaManager alloc] init]; - } - - return g_siwaManager; - } - - API_AVAILABLE(ios(13.0)) - // Kicks off the request to check the credential state of a provided user id. - void DoCheckStatus(lua_State* L, char* userID, int callback, int context, lua_State* thread) { - { - DM_MUTEX_SCOPED_LOCK(g_siwaMutex) - if(g_SiwaData.m_waitingOnCallback) - { - dmLogError("ERROR: Callback already in progress, abort"); - return; - } - - CallbackSetup(L); - - g_SiwaData.m_userID = userID; //taking ownership - g_SiwaData.m_Callback = callback; - g_SiwaData.m_Self = context; - g_SiwaData.m_MainThread = thread; - - g_SiwaData.m_waitingOnCallback = true; - } - - SiwaManager *siwaMan = GetSiwaManager(); - [siwaMan checkCredentialStatus]; - return; - } - - API_AVAILABLE(ios(13.0)) - // Kicks off the sign in with apple flow. - void DoAuthentication(lua_State* L, int callback, int context, lua_State* thread){ - { - DM_MUTEX_SCOPED_LOCK(g_siwaMutex) - if(g_SiwaData.m_waitingOnCallback) - { - dmLogError("ERROR: Callback already in progress, abort"); - return; - } - - CallbackSetup(L); - - g_SiwaData.m_Callback = callback; - g_SiwaData.m_Self = context; - g_SiwaData.m_MainThread = thread; - - g_SiwaData.m_waitingOnCallback = true; - } - - SiwaManager *siwaMan = GetSiwaManager(); - [siwaMan loginWithUI]; + g_SiwaManager = [[SiwaManager alloc] init]; } - int CheckStatusOfAppleID(lua_State* L, char* userID, int callback, int context, lua_State* thread) { - DM_LUA_STACK_CHECK(L, 0); - - if(IsSiwaAvailable()) - { - DoCheckStatus(L, userID, callback, context, thread); - } - - return 0; - } + return g_SiwaManager; +} - int AuthenticateWithApple(lua_State* L, int callback, int context, lua_State* thread) { - DM_LUA_STACK_CHECK(L, 0); +API_AVAILABLE(ios(13.0)) +// Kicks off the request to check the credential state of a provided user id. +void SiwaDoCheckStatus() { + SiwaManager *siwaMan = GetSiwaManager(); + [siwaMan checkCredentialStatus]; + return; +} - if(IsSiwaAvailable()) - { - DoAuthentication(L, callback, context, thread); - } +API_AVAILABLE(ios(13.0)) +// Kicks off the sign in with apple flow. +void SiwaDoAuthentication() { + SiwaManager *siwaMan = GetSiwaManager(); + [siwaMan loginWithUI]; +} - return 0; - } +void SiwaCheckStatusOfAppleID() { + SiwaDoCheckStatus(); +} -} // namespace -} // namespace +void SiwaAuthenticateWithApple() { + SiwaDoAuthentication(); +} #endif diff --git a/siwa/src/siwa_main.cpp b/siwa/src/siwa_main.cpp deleted file mode 100755 index e745a5b..0000000 --- a/siwa/src/siwa_main.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#if defined(DM_PLATFORM_IOS) - -#include "siwa_ios.h" -#include -#include -#include -#include -#include - - -#define MODULE_NAME "siwa" - -namespace siwa_code { - -static int ReturnFalse(lua_State* L) { - DM_LUA_STACK_CHECK(L, 1); - lua_pushboolean(L, 0); - return 1; -} - -static int ReturnNil(lua_State* L){ - DM_LUA_STACK_CHECK(L, 1); - lua_pushnil(L); - return 1; -} - -// Function for asking Apple if a provided user id currently grants us access to it for this app. -// Expects: (a string containing the user id, and a callback that expects a self instance and a table containing results). -// The callback will be triggered at some point later after getting a response from Apple. -// The response will be SUCCESS whether or not the id granted us permission. -// It will give us a status of 1 if the user is authorized, and 2 if it's not. -static int CheckStatusOfAppleID(lua_State* L){ - DM_LUA_STACK_CHECK(L, 0); - - int num_args = lua_gettop(L); - if(num_args != 2){ - dmLogError("Incorrect number of args passed to CheckStatusOfAppleID!"); - return ReturnFalse(L); - } - - luaL_checktype(L, 1, LUA_TSTRING); - char* userID = strdup(lua_tostring(L, 1)); - - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_pushvalue(L, 2); - int callback = dmScript::Ref(L, LUA_REGISTRYINDEX); - - dmScript::GetInstance(L); - int context = dmScript::Ref(L, LUA_REGISTRYINDEX); - - lua_State* thread = dmScript::GetMainThread(L); - - if (platform::IsSiwaSupported(L)) { - platform::CheckStatusOfAppleID(L, userID, callback, context, thread); - } - - return 0; -} - -// Function for having a user sign in with Apple. -// Expects: (a callback that expects a self instance and a table containing results). -// The callback will be triggered at some point later after getting a response from Apple. -// The first time a user signs in after granting permission to this app, the callback will give us a name and email -// as well as a user id and identity token on success. -// On subsequent logins, we will only get the user id and identity token. -// On a failure, such as when the user cancels the login flow, we will get ERROR as a result, and a message -// describing the error. -static int AuthenticateWithApple(lua_State* L){ - DM_LUA_STACK_CHECK(L, 0); - - luaL_checktype(L, 1, LUA_TFUNCTION); - - lua_pushvalue(L, 1); - int callback = dmScript::Ref(L, LUA_REGISTRYINDEX); - - dmScript::GetInstance(L); - int context = dmScript::Ref(L, LUA_REGISTRYINDEX); - - lua_State* thread = dmScript::GetMainThread(L); - - if (platform::IsSiwaSupported(L)) { - platform::AuthenticateWithApple(L, callback, context, thread); - } - - return 0; -} - -const luaL_reg lua_register[] = -{ - {"is_siwa_supported", platform::IsSiwaSupported}, - {"check_credentials_status", CheckStatusOfAppleID}, - {"authenticate", AuthenticateWithApple}, - {0, 0} -}; - -static dmExtension::Result AppInitialize(dmExtension::AppParams* params) -{ - platform::AppInitialize(); - return dmExtension::RESULT_OK; -} - -static dmExtension::Result AppFinalize(dmExtension::AppParams* params) -{ - platform::AppFinalize(); - return dmExtension::RESULT_OK; -} - -static dmExtension::Result Initialize(dmExtension::Params* params) -{ - // Register lua functions - { - lua_State* L = params->m_L; - int top = lua_gettop(L); - luaL_register(L, MODULE_NAME, lua_register); - lua_pop(L, 1); - assert(top == lua_gettop(L)); - } - - platform::Initialize(); - return dmExtension::RESULT_OK; -} - -static dmExtension::Result Update(dmExtension::Params* params) -{ - platform::Update(); - return dmExtension::RESULT_OK; -} - -static dmExtension::Result Finalize(dmExtension::Params* params) -{ - platform::Finalize(); - return dmExtension::RESULT_OK; -} - -} // namespace - -DM_DECLARE_EXTENSION(siwa, MODULE_NAME, siwa_code::AppInitialize, siwa_code::AppFinalize, siwa_code::Initialize, siwa_code::Update, 0, siwa_code::Finalize); - -#endif From 13abd9ca2d6a13bae7e6c0186cb5e2ea4c000c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Ritzl?= Date: Sun, 23 Aug 2020 23:48:51 +0200 Subject: [PATCH 2/6] Docs and additional cleanup --- .github/FUNDING.yml | 3 + .github/workflows/trigger-site-rebuild.yml | 19 ++++++ docs/index.md | 29 +++++++++ main/main.gui_script | 12 ++-- siwa/api/siwa.script_api | 75 +++++++++++++++++++++ siwa/src/siwa.cpp | 76 ++++++++-------------- siwa/src/siwa.h | 28 ++++---- siwa/src/siwa_ios.mm | 42 ++++++------ 8 files changed, 192 insertions(+), 92 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/trigger-site-rebuild.yml create mode 100644 docs/index.md create mode 100644 siwa/api/siwa.script_api diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..ee9a439 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: defold +patreon: Defold +custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NBNBHTUW4GS4C'] diff --git a/.github/workflows/trigger-site-rebuild.yml b/.github/workflows/trigger-site-rebuild.yml new file mode 100644 index 0000000..ec697cc --- /dev/null +++ b/.github/workflows/trigger-site-rebuild.yml @@ -0,0 +1,19 @@ +name: Trigger site rebuild + +on: [push] + +jobs: + site-rebuild: + runs-on: ubuntu-latest + + steps: [ + { + name: 'Repository dispatch', + uses: defold/repository-dispatch@1.2.1, + with: { + repo: 'defold/defold.github.io', + token: '${{ secrets.SERVICES_GITHUB_TOKEN }}', + user: 'services@defold.se', + action: 'extension-siwa' + } + }] diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..eea7daa --- /dev/null +++ b/docs/index.md @@ -0,0 +1,29 @@ +--- +title: Sign in with Apple extension for Defold +brief: This manual covers how to setup and use Sign in with Apple in Defold. +--- + +# Sign in with Apple extension for Defold + +This extension provides functions to use [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) to allow users to set up an account and sign in to your game with their Apple ID. + +## Installation + +To use this library in your Defold project, add the following URL to your `game.project` dependencies: + +https://github.com/defold/extension-siwa/archive/master.zip + +We recommend using a link to a zip file of a [specific release](https://github.com/defold/extension-siwa/releases). + + +## Setting up your app for Sign in with Apple + +To get started you need to enable your app’s App ID with the Sign in with Apple capability. [Follow the official Apple developer instructions](https://help.apple.com/developer-account/?lang=en#/devde676e696) to get started. + + +## Source code + +The source code is available on [GitHub](https://github.com/defold/extension-siwa) + + +## API reference diff --git a/main/main.gui_script b/main/main.gui_script index b2c083f..6480b6b 100755 --- a/main/main.gui_script +++ b/main/main.gui_script @@ -4,15 +4,15 @@ local lines = {} local function log(msg, ...) msg = msg:format(...) print(msg) - + table.insert(lines, 1, msg) table.remove(lines, 10) gui.set_text(gui.get_node("test_text"), table.concat(lines, "\n")) end -local function check_credentials_status(self, id) - log("check_credentials_status %s", id) - siwa.check_credentials_status(id, function(self, data) +local function get_credential_state(self, id) + log("get_credential_state %s", id) + siwa.get_credential_state(id, function(self, data) for k,v in pairs(data) do log("%s: %s", k, tostring(v)) end @@ -30,7 +30,7 @@ local function authenticate(self) end local function is_siwa_supported() - return siwa and siwa.is_siwa_supported() + return siwa and siwa.is_supported() end function init(self) @@ -47,7 +47,7 @@ function on_input(self, action_id, action) if is_siwa_supported() then dl:button("check", action_id, action, function() if self.user_id then - check_credentials_status(self, self.user_id) + get_credential_state(self, self.user_id) else log("No user id. Login first") end diff --git a/siwa/api/siwa.script_api b/siwa/api/siwa.script_api new file mode 100644 index 0000000..5e57292 --- /dev/null +++ b/siwa/api/siwa.script_api @@ -0,0 +1,75 @@ +- name: siwa + type: table + desc: Functions and constants for interacting Sign in with Apple. + [icon:ios] + members: + +#***************************************************************************************************** + + - name: is_supported + type: function + desc: Check if Sign in with Apple is available (iOS 13+). + + +#***************************************************************************************************** + + - name: get_credential_state + type: function + desc: Get the credential state of a user. + + parameters: + - name: user_id + type: string + desc: User id to get credential state for. + - name: callback + type: function + desc: Credential state callback function. + parameters: + - name: self + type: object + desc: The current object. + + - name: state + type: table + desc: The credential state (fields: user_id, credential_state) + +#***************************************************************************************************** + + - name: authenticate + type: function + desc: Show the Sign in with Apple UI + + parameters: + - name: callback + type: function + desc: Authentication callback function. + parameters: + - name: self + type: object + desc: The current object. + + - name: state + type: table + desc: The authentication result data (fields: user_id, identity_token, email, first_name, family_name, status, result) + + +#***************************************************************************************************** + + - name: STATE_NOT_FOUND + type: number + desc: Credential state not found for the provided user id. + + + - name: STATE_UNKNOWN + type: number + desc: Unknown credential state. + + + - name: STATE_AUTHORIZED + type: number + desc: The provided user id has authorized the app. + + + - name: STATE_REVOKED + type: number + desc: The provided user id has revoked credential for the app. diff --git a/siwa/src/siwa.cpp b/siwa/src/siwa.cpp index 7dd47fd..1e01497 100755 --- a/siwa/src/siwa.cpp +++ b/siwa/src/siwa.cpp @@ -3,19 +3,17 @@ #include "siwa.h" #include - #define MODULE_NAME "siwa" SiwaData g_SiwaData; SiwaCallbackData g_SiwaCallbackData; - -char* SiwaGetUserId() +char* Siwa_GetUserId() { return g_SiwaData.m_userID; } -void SiwaResetCallbackData() +void Siwa_ResetCallbackData() { free(g_SiwaCallbackData.m_userID); g_SiwaCallbackData.m_userID = 0; @@ -39,7 +37,7 @@ void SiwaResetCallbackData() g_SiwaCallbackData.m_cmd = CMD_NONE; } -void SiwaQueueCredentialCallback(char* userID, SiwaCredentialState state) +void Siwa_QueueCredentialCallback(char* userID, SiwaCredentialState state) { if(g_SiwaCallbackData.m_cmd != CMD_NONE) { dmLogError("Can't queue credential callback, already have a callback queued!"); @@ -52,7 +50,7 @@ void SiwaQueueCredentialCallback(char* userID, SiwaCredentialState state) } -void SiwaQueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus) +void Siwa_QueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus) { if(g_SiwaCallbackData.m_cmd != CMD_NONE) { dmLogError("Can't queue auth success callback, already have a callback queued!"); @@ -69,7 +67,7 @@ void SiwaQueueAuthSuccessCallback(const char* identityToken, const char* userID, g_SiwaCallbackData.m_message = strdup(""); } -void SiwaQueueAuthFailureCallback(const char* message) +void Siwa_QueueAuthFailureCallback(const char* message) { if(g_SiwaCallbackData.m_cmd != CMD_NONE) { dmLogError("Can't queue auth error callback, already have a callback queued!"); @@ -81,7 +79,7 @@ void SiwaQueueAuthFailureCallback(const char* message) } -void SiwaRunCallback() +void Siwa_TriggerCallback() { lua_State* L = dmScript::GetCallbackLuaContext(g_SiwaData.m_callback); DM_LUA_STACK_CHECK(L, 0); @@ -100,7 +98,7 @@ void SiwaRunCallback() lua_pushstring(L, g_SiwaCallbackData.m_userID); lua_settable(L, -3); - lua_pushstring(L, "credentials_state"); + lua_pushstring(L, "credential_state"); lua_pushnumber(L, g_SiwaCallbackData.m_state); lua_settable(L, -3); } @@ -155,7 +153,7 @@ void SiwaRunCallback() } -void SiwaSetupCallback(lua_State* L, int index) +void Siwa_SetupCallback(lua_State* L, int index) { if (g_SiwaData.m_callback) { dmScript::DestroyCallback(g_SiwaData.m_callback); @@ -163,23 +161,17 @@ void SiwaSetupCallback(lua_State* L, int index) g_SiwaData.m_callback = dmScript::CreateCallback(L, index); } -void SiwaCleanupCallback() { +void Siwa_CleanupCallback() { if (g_SiwaData.m_callback) { dmScript::DestroyCallback(g_SiwaData.m_callback); g_SiwaData.m_callback = 0; } } - -// Function for asking Apple if a provided user id currently grants us access to it for this app. -// Expects: (a string containing the user id, and a callback that expects a self instance and a table containing results). -// The callback will be triggered at some point later after getting a response from Apple. -// The response will be SUCCESS whether or not the id granted us permission. -// It will give us a status of 1 if the user is authorized, and 2 if it's not. -int SiwaCheckStatusOfAppleID(lua_State* L){ +int Siwa_GetCredentialState(lua_State* L){ DM_LUA_STACK_CHECK(L, 1); - if (!SiwaIsAvailable()) { + if (!Siwa_PlatformIsSupported()) { dmLogWarning("Sign in with Apple is not available"); lua_pushboolean(L, 0); return 1; @@ -192,37 +184,22 @@ int SiwaCheckStatusOfAppleID(lua_State* L){ return 1; } - int num_args = lua_gettop(L); - if(num_args != 2) { - dmLogError("Incorrect number of args passed to SiwaCheckStatusOfAppleID!"); - lua_pushboolean(L, 0); - return 1; - } - luaL_checktype(L, 1, LUA_TSTRING); if (g_SiwaData.m_userID) free(g_SiwaData.m_userID); g_SiwaData.m_userID = strdup(lua_tostring(L, 1)); luaL_checktype(L, 2, LUA_TFUNCTION); - SiwaSetupCallback(L, 2); - SiwaCheckStatusOfAppleID(); + Siwa_SetupCallback(L, 2); + Siwa_PlatformGetCredentialState(); lua_pushboolean(L, 1); return 1; } -// Function for having a user sign in with Apple. -// Expects: (a callback that expects a self instance and a table containing results). -// The callback will be triggered at some point later after getting a response from Apple. -// The first time a user signs in after granting permission to this app, the callback will give us a name and email -// as well as a user id and identity token on success. -// On subsequent logins, we will only get the user id and identity token. -// On a failure, such as when the user cancels the login flow, we will get ERROR as a result, and a message -// describing the error. -int SiwaAuthenticateWithApple(lua_State* L) { +int Siwa_AuthenticateWithApple(lua_State* L) { DM_LUA_STACK_CHECK(L, 1); - if (!SiwaIsAvailable()) { + if (!Siwa_PlatformIsSupported()) { dmLogWarning("Sign in with Apple is not available"); lua_pushboolean(L, 0); return 1; @@ -236,23 +213,22 @@ int SiwaAuthenticateWithApple(lua_State* L) { } luaL_checktype(L, 1, LUA_TFUNCTION); - SiwaSetupCallback(L, 1); - SiwaAuthenticateWithApple(); + Siwa_SetupCallback(L, 1); + Siwa_PlatformAuthenticateWithApple(); lua_pushboolean(L, 1); return 1; } -int SiwaIsSupported(lua_State* L) { +int Siwa_IsSupported(lua_State* L) { DM_LUA_STACK_CHECK(L, 1); - lua_pushboolean(L, SiwaIsAvailable()); + lua_pushboolean(L, Siwa_PlatformIsSupported()); return 1; } - static dmExtension::Result SiwaAppInitialize(dmExtension::AppParams* params) { - SiwaResetCallbackData(); + Siwa_ResetCallbackData(); return dmExtension::RESULT_OK; } @@ -263,9 +239,9 @@ static dmExtension::Result SiwaAppFinalize(dmExtension::AppParams* params) const luaL_reg lua_register[] = { - {"is_siwa_supported", SiwaIsSupported}, - {"check_credentials_status", SiwaCheckStatusOfAppleID}, - {"authenticate", SiwaAuthenticateWithApple}, + {"is_supported", Siwa_IsSupported}, + {"get_credential_state", Siwa_GetCredentialState}, + {"authenticate", Siwa_AuthenticateWithApple}, {0, 0} }; @@ -295,9 +271,9 @@ static dmExtension::Result SiwaUpdate(dmExtension::Params* params) { if(g_SiwaCallbackData.m_cmd != CMD_NONE) { - SiwaRunCallback(); - SiwaResetCallbackData(); - SiwaCleanupCallback(); + Siwa_TriggerCallback(); + Siwa_ResetCallbackData(); + Siwa_CleanupCallback(); } return dmExtension::RESULT_OK; } diff --git a/siwa/src/siwa.h b/siwa/src/siwa.h index 00284f1..b47c76f 100755 --- a/siwa/src/siwa.h +++ b/siwa/src/siwa.h @@ -58,36 +58,34 @@ struct SiwaData dmScript::LuaCallbackInfo* m_callback; }; -char* SiwaGetUserId(); +char* Siwa_GetUserId(); -void SiwaCheckForQueuedCallbacks(); -void SiwaInitCallbackData(); -void SiwaResetCallbackData(); +void Siwa_CheckForQueuedCallbacks(); +void Siwa_InitCallbackData(); +void Siwa_ResetCallbackData(); // Queue the credential check callback to be triggered next update call in the main thread. -void SiwaQueueCredentialCallback(char* userID, SiwaCredentialState state); +void Siwa_QueueCredentialCallback(char* userID, SiwaCredentialState state); // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization succeeds. -void SiwaQueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus); +void Siwa_QueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus); // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization fails. -void SiwaQueueAuthFailureCallback(const char* message); +void Siwa_QueueAuthFailureCallback(const char* message); -void SiwaRunCallback(); - -void SiwaSetupCallback(lua_State* L, int index); - -void SiwaCleanupCallback(); +void Siwa_TriggerCallback(); +void Siwa_SetupCallback(lua_State* L, int index); +void Siwa_CleanupCallback(); // Trigged by a call from lua to check if sign in with apple is supported on this device. -bool SiwaIsAvailable(); +bool Siwa_PlatformIsSupported(); // Triggered by a call from lua to start the sign in with apple flow // expects the callback to be a reference number to the lua registry // expects the context to be reference number to the lua registry -void SiwaAuthenticateWithApple(); +void Siwa_PlatformAuthenticateWithApple(); // Triggered by a call from lua to check if a provided apple id grants this app permission to use that id. // expects the callback to be a reference number to the lua registry // expects the context to be reference number to the lua registry -void SiwaCheckStatusOfAppleID(); +void Siwa_PlatformGetCredentialState(); #endif diff --git a/siwa/src/siwa_ios.mm b/siwa/src/siwa_ios.mm index ea5af02..f2c927b 100755 --- a/siwa/src/siwa_ios.mm +++ b/siwa/src/siwa_ios.mm @@ -37,9 +37,9 @@ - (instancetype) init // The possible results are revoked(0), authorized(1), and unknown(2). // In practice, we have recieved unknown when revoking permission to this test app // so we should treat both revoked and unknown as unauthorized. -- (void) checkCredentialStatus +- (void) getCredentialState { - char* userId = SiwaGetUserId(); + char* userId = Siwa_GetUserId(); NSString* user_id_string = [[NSString alloc] initWithUTF8String:userId]; [self.m_idProvider getCredentialStateForUserID: user_id_string @@ -70,7 +70,7 @@ - (void) checkCredentialStatus break; } - SiwaQueueCredentialCallback(userId, state); + Siwa_QueueCredentialCallback(userId, state); }]; } @@ -108,30 +108,23 @@ - (void)authorizationController:(ASAuthorizationController *)controller NSString* tokenString = [[NSString alloc] initWithData:appleIdCredential.identityToken encoding:NSUTF8StringEncoding]; const char* identityToken = [tokenString UTF8String]; - SiwaQueueAuthSuccessCallback(identityToken, appleUserId, email, givenName, familyName, userDetectionStatus); + Siwa_QueueAuthSuccessCallback(identityToken, appleUserId, email, givenName, familyName, userDetectionStatus); } else { - SiwaQueueAuthFailureCallback("authorization failed!"); + Siwa_QueueAuthFailureCallback("authorization failed!"); } } // The Auth controller callback for getting an error during authorization - (void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error { - SiwaQueueAuthFailureCallback("authorization error!"); + Siwa_QueueAuthFailureCallback("authorization error!"); } @end -// Checks if Siwa is supported on this device by seeing if the main -// class involved in all the siwa requests we use exists. -bool SiwaIsAvailable() -{ - return ([ASAuthorizationAppleIDProvider class] != nil); -} - API_AVAILABLE(ios(13.0)) static SiwaManager* g_SiwaManager = nil; @@ -147,26 +140,33 @@ bool SiwaIsAvailable() } API_AVAILABLE(ios(13.0)) -// Kicks off the request to check the credential state of a provided user id. -void SiwaDoCheckStatus() { +// Kicks off the request to get the credential state of a provided user id. +void Siwa_PlatformDoGetCredentialState() { SiwaManager *siwaMan = GetSiwaManager(); - [siwaMan checkCredentialStatus]; + [siwaMan getCredentialState]; return; } API_AVAILABLE(ios(13.0)) // Kicks off the sign in with apple flow. -void SiwaDoAuthentication() { +void Siwa_PlatformDoAuthenticateWithApple() { SiwaManager *siwaMan = GetSiwaManager(); [siwaMan loginWithUI]; } -void SiwaCheckStatusOfAppleID() { - SiwaDoCheckStatus(); +// Checks if Siwa is supported on this device by seeing if the main +// class involved in all the siwa requests we use exists. +bool Siwa_PlatformIsSupported() +{ + return ([ASAuthorizationAppleIDProvider class] != nil); +} + +void Siwa_PlatformGetCredentialState() { + Siwa_PlatformDoGetCredentialState(); } -void SiwaAuthenticateWithApple() { - SiwaDoAuthentication(); +void Siwa_PlatformAuthenticateWithApple() { + Siwa_PlatformDoAuthenticateWithApple(); } #endif From d5406aa5664f089bab6b33e573a9794a6e4b65fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Ritzl?= Date: Sun, 23 Aug 2020 23:54:12 +0200 Subject: [PATCH 3/6] Update README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b1c99d8..035529b 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ -# Sign in with Apple -Native extension to support the Sign in with Apple feature in Defold. +# Sign in with Apple extension for Defold -"Sign in with Apple makes it easy for users to sign in to your apps and websites using their Apple ID. Instead of filling out forms, verifying email addresses, and choosing new passwords, they can use Sign in with Apple to set up an account and start using your app right away. All accounts are protected with two-factor authentication for superior security, and Apple will not track users’ activity in your app or website" [Apple Developer docs](https://developer.apple.com/sign-in-with-apple/) +Defold [native extension](https://www.defold.com/manuals/extensions/) which provides access to Sign in with Apple functionality on iOS. -"Apps that use a third-party or social login service (such as Facebook Login, Google Sign-In, Sign in with Twitter, Sign In with LinkedIn, Login with Amazon, or WeChat Login) to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option. A user’s primary account is the account they establish with your app for the purposes of identifying themselves, signing in, and accessing your features and associated services." [Apple review guidelines](https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple) +[Manual, API and setup instructions](https://www.defold.com/extension-siwa/) is available on the official Defold site. From 5e5e14841ee801421c4787f6e97c6ac42bdb8f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Ritzl?= Date: Mon, 24 Aug 2020 00:08:13 +0200 Subject: [PATCH 4/6] Added user status constants --- siwa/api/siwa.script_api | 21 ++++++++++++++++++--- siwa/src/siwa.cpp | 10 +++++++--- siwa/src/siwa.h | 15 ++++++++++++--- siwa/src/siwa_ios.mm | 12 +++++++++++- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/siwa/api/siwa.script_api b/siwa/api/siwa.script_api index 5e57292..08c7a4d 100644 --- a/siwa/api/siwa.script_api +++ b/siwa/api/siwa.script_api @@ -57,7 +57,7 @@ - name: STATE_NOT_FOUND type: number - desc: Credential state not found for the provided user id. + desc: The user can’t be found. - name: STATE_UNKNOWN @@ -67,9 +67,24 @@ - name: STATE_AUTHORIZED type: number - desc: The provided user id has authorized the app. + desc: The user is authorized. - name: STATE_REVOKED type: number - desc: The provided user id has revoked credential for the app. + desc: Authorization for the given user has been revoked. + + + - name: STATUS_UNKNOWN + type: number + desc: The system hasn’t determined whether the user might be a real person. + + + - name: STATUS_UNSUPPORTED + type: number + desc: The system can’t determine this user’s status as a real person. + + + - name: STATUS_LIKELY_REAL + type: number + desc: The user appears to be a real person. diff --git a/siwa/src/siwa.cpp b/siwa/src/siwa.cpp index 1e01497..094e8c4 100755 --- a/siwa/src/siwa.cpp +++ b/siwa/src/siwa.cpp @@ -32,12 +32,12 @@ void Siwa_ResetCallbackData() free(g_SiwaCallbackData.m_message); g_SiwaCallbackData.m_message = 0; - g_SiwaCallbackData.m_userStatus = -1; + g_SiwaCallbackData.m_userStatus = STATUS_UNSUPPORTED; g_SiwaCallbackData.m_state = STATE_UNKNOWN; g_SiwaCallbackData.m_cmd = CMD_NONE; } -void Siwa_QueueCredentialCallback(char* userID, SiwaCredentialState state) +void Siwa_QueueCredentialCallback(const char* userID, const SiwaCredentialState state) { if(g_SiwaCallbackData.m_cmd != CMD_NONE) { dmLogError("Can't queue credential callback, already have a callback queued!"); @@ -50,7 +50,7 @@ void Siwa_QueueCredentialCallback(char* userID, SiwaCredentialState state) } -void Siwa_QueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus) +void Siwa_QueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, const SiwaUserDetectionStatus userStatus) { if(g_SiwaCallbackData.m_cmd != CMD_NONE) { dmLogError("Can't queue auth success callback, already have a callback queued!"); @@ -260,6 +260,10 @@ static dmExtension::Result SiwaInitialize(dmExtension::Params* params) SETCONSTANT(STATE_AUTHORIZED) SETCONSTANT(STATE_REVOKED) + SETCONSTANT(STATUS_UNKNOWN) + SETCONSTANT(STATUS_UNSUPPORTED) + SETCONSTANT(STATUS_LIKELY_REAL) + #undef SETCONSTANT lua_pop(L, 1); diff --git a/siwa/src/siwa.h b/siwa/src/siwa.h index b47c76f..9b1c6ea 100755 --- a/siwa/src/siwa.h +++ b/siwa/src/siwa.h @@ -11,6 +11,7 @@ enum SiwaCallbackCmd CMD_AUTH_FAILED = 3 }; +// https://developer.apple.com/documentation/authenticationservices/asauthorizationappleidprovidercredentialstate/asauthorizationappleidprovidercredentialauthorized?language=objc enum SiwaCredentialState { STATE_UNKNOWN = 0, @@ -19,6 +20,14 @@ enum SiwaCredentialState STATE_NOT_FOUND = 3 }; +// https://developer.apple.com/documentation/authenticationservices/asuserdetectionstatus?language=objc +enum SiwaUserDetectionStatus +{ + STATUS_UNSUPPORTED = 0, + STATUS_LIKELY_REAL = 1, + STATUS_UNKNOWN = 2 +}; + struct SiwaCallbackData { SiwaCallbackData() @@ -28,13 +37,13 @@ struct SiwaCallbackData SiwaCallbackCmd m_cmd; SiwaCredentialState m_state; + SiwaUserDetectionStatus m_userStatus; char* m_identityToken; char* m_userID; char* m_email; char* m_firstName; char* m_familyName; - int m_userStatus; char* m_message; }; @@ -65,9 +74,9 @@ void Siwa_InitCallbackData(); void Siwa_ResetCallbackData(); // Queue the credential check callback to be triggered next update call in the main thread. -void Siwa_QueueCredentialCallback(char* userID, SiwaCredentialState state); +void Siwa_QueueCredentialCallback(const char* userID, const SiwaCredentialState state); // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization succeeds. -void Siwa_QueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, int userStatus); +void Siwa_QueueAuthSuccessCallback(const char* identityToken, const char* userID, const char* email, const char* firstName, const char* familyName, const SiwaUserDetectionStatus userStatus); // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization fails. void Siwa_QueueAuthFailureCallback(const char* message); diff --git a/siwa/src/siwa_ios.mm b/siwa/src/siwa_ios.mm index f2c927b..a258265 100755 --- a/siwa/src/siwa_ios.mm +++ b/siwa/src/siwa_ios.mm @@ -104,7 +104,17 @@ - (void)authorizationController:(ASAuthorizationController *)controller const char* email = [appleIdCredential.email UTF8String]; const char* givenName = [appleIdCredential.fullName.givenName UTF8String]; const char* familyName = [appleIdCredential.fullName.familyName UTF8String]; - const int userDetectionStatus = (int) appleIdCredential.realUserStatus; + SiwaUserDetectionStatus userDetectionStatus = STATUS_UNSUPPORTED; + if (appleIdCredential.realUserStatus == ASUserDetectionStatusLikelyReal) + { + userDetectionStatus = STATUS_LIKELY_REAL; + } + else if (appleIdCredential.realUserStatus == ASUserDetectionStatusUnknown) + { + userDetectionStatus = STATUS_UNKNOWN; + } + + appleIdCredential.realUserStatus; NSString* tokenString = [[NSString alloc] initWithData:appleIdCredential.identityToken encoding:NSUTF8StringEncoding]; const char* identityToken = [tokenString UTF8String]; From 0cd19264055704199acd7abb275ad58c51fff5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Ritzl?= Date: Mon, 24 Aug 2020 09:08:57 +0200 Subject: [PATCH 5/6] Cleaned up siwa header --- siwa/src/siwa.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/siwa/src/siwa.h b/siwa/src/siwa.h index 9b1c6ea..767c029 100755 --- a/siwa/src/siwa.h +++ b/siwa/src/siwa.h @@ -48,12 +48,6 @@ struct SiwaCallbackData char* m_message; }; -// Data used to ensure we only have 1 request in progress with Apples servers at a time. -// Access should be restricted behind mutex protection as the callbacks from apple can -// be triggered in arbitrary contexts. -// This data is also used to hold information passed from lua until the callback comes -// back from Apple, so that we may pass it back to lua to identify the request we are -// getting a callback for. struct SiwaData { SiwaData() @@ -69,10 +63,6 @@ struct SiwaData char* Siwa_GetUserId(); -void Siwa_CheckForQueuedCallbacks(); -void Siwa_InitCallbackData(); -void Siwa_ResetCallbackData(); - // Queue the credential check callback to be triggered next update call in the main thread. void Siwa_QueueCredentialCallback(const char* userID, const SiwaCredentialState state); // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization succeeds. @@ -80,10 +70,6 @@ void Siwa_QueueAuthSuccessCallback(const char* identityToken, const char* userID // Queue the sign in authorization callback to be triggered next update call in the main thread, when authorization fails. void Siwa_QueueAuthFailureCallback(const char* message); -void Siwa_TriggerCallback(); -void Siwa_SetupCallback(lua_State* L, int index); -void Siwa_CleanupCallback(); - // Trigged by a call from lua to check if sign in with apple is supported on this device. bool Siwa_PlatformIsSupported(); From d553fba94ce54f899e10028fab560dac4de1defc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bjo=CC=88rn=20Ritzl?= Date: Mon, 24 Aug 2020 16:16:06 +0200 Subject: [PATCH 6/6] Made a couple of functions static --- siwa/src/siwa.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/siwa/src/siwa.cpp b/siwa/src/siwa.cpp index 094e8c4..882da87 100755 --- a/siwa/src/siwa.cpp +++ b/siwa/src/siwa.cpp @@ -13,7 +13,7 @@ char* Siwa_GetUserId() return g_SiwaData.m_userID; } -void Siwa_ResetCallbackData() +static void Siwa_ResetCallbackData() { free(g_SiwaCallbackData.m_userID); g_SiwaCallbackData.m_userID = 0; @@ -79,7 +79,7 @@ void Siwa_QueueAuthFailureCallback(const char* message) } -void Siwa_TriggerCallback() +static void Siwa_TriggerCallback() { lua_State* L = dmScript::GetCallbackLuaContext(g_SiwaData.m_callback); DM_LUA_STACK_CHECK(L, 0); @@ -153,7 +153,7 @@ void Siwa_TriggerCallback() } -void Siwa_SetupCallback(lua_State* L, int index) +static void Siwa_SetupCallback(lua_State* L, int index) { if (g_SiwaData.m_callback) { dmScript::DestroyCallback(g_SiwaData.m_callback); @@ -161,14 +161,14 @@ void Siwa_SetupCallback(lua_State* L, int index) g_SiwaData.m_callback = dmScript::CreateCallback(L, index); } -void Siwa_CleanupCallback() { +static void Siwa_CleanupCallback() { if (g_SiwaData.m_callback) { dmScript::DestroyCallback(g_SiwaData.m_callback); g_SiwaData.m_callback = 0; } } -int Siwa_GetCredentialState(lua_State* L){ +static int Siwa_GetCredentialState(lua_State* L){ DM_LUA_STACK_CHECK(L, 1); if (!Siwa_PlatformIsSupported()) { @@ -196,7 +196,7 @@ int Siwa_GetCredentialState(lua_State* L){ return 1; } -int Siwa_AuthenticateWithApple(lua_State* L) { +static int Siwa_AuthenticateWithApple(lua_State* L) { DM_LUA_STACK_CHECK(L, 1); if (!Siwa_PlatformIsSupported()) { @@ -220,7 +220,7 @@ int Siwa_AuthenticateWithApple(lua_State* L) { return 1; } -int Siwa_IsSupported(lua_State* L) { +static int Siwa_IsSupported(lua_State* L) { DM_LUA_STACK_CHECK(L, 1); lua_pushboolean(L, Siwa_PlatformIsSupported()); return 1;