Initial push
This commit is contained in:
parent
13844c3e93
commit
1525e40378
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
github: defold
|
||||||
|
patreon: Defold
|
||||||
|
custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=NBNBHTUW4GS4C']
|
19
.github/workflows/trigger-site-rebuild.yml
vendored
Normal file
19
.github/workflows/trigger-site-rebuild.yml
vendored
Normal file
@ -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'
|
||||||
|
}
|
||||||
|
}]
|
10
.gitignore
vendored
Executable file
10
.gitignore
vendored
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/.internal
|
||||||
|
/build
|
||||||
|
.externalToolBuilders
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.lock-wscript
|
||||||
|
*.pyc
|
||||||
|
.project
|
||||||
|
.cproject
|
||||||
|
builtins
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Defold
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
66
docs/index.md
Normal file
66
docs/index.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Check Sign in with Apple support
|
||||||
|
|
||||||
|
```Lua
|
||||||
|
if siwa.is_supported() then
|
||||||
|
print("Sign in with Apple is supported")
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Trigger Sign in with Apple
|
||||||
|
|
||||||
|
```Lua
|
||||||
|
siwa.authenticate(id, function(self, data)
|
||||||
|
print(data.identity_token)
|
||||||
|
print(data.user_id)
|
||||||
|
print(data.first_name, data.family_name)
|
||||||
|
print(data.email)
|
||||||
|
if data.user_status == siwa.STATUS_LIKELY_REAL then
|
||||||
|
print("Likely a real person")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check credential state
|
||||||
|
|
||||||
|
```Lua
|
||||||
|
siwa.get_credential_state(id, function(self, data)
|
||||||
|
if data.credential_state == siwa.STATE_AUTHORIZED then
|
||||||
|
print("User has still authorized the application", data.user_id)
|
||||||
|
elseif data.credential_state == siwa.STATE_REVOKED then
|
||||||
|
print("User has revoked authorization for the application", data.user_id)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Source code
|
||||||
|
|
||||||
|
The source code is available on [GitHub](https://github.com/defold/extension-siwa)
|
||||||
|
|
||||||
|
|
||||||
|
## API reference
|
20
game.project
Executable file
20
game.project
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
[project]
|
||||||
|
title = extension-siwa
|
||||||
|
dependencies = https://github.com/andsve/dirtylarry/archive/master.zip
|
||||||
|
|
||||||
|
[script]
|
||||||
|
shared_state = 1
|
||||||
|
|
||||||
|
[library]
|
||||||
|
include_dirs = siwa
|
||||||
|
|
||||||
|
[bootstrap]
|
||||||
|
main_collection = /main/main.collectionc
|
||||||
|
|
||||||
|
[display]
|
||||||
|
width = 640
|
||||||
|
height = 1136
|
||||||
|
|
||||||
|
[ios]
|
||||||
|
bundle_identifier = com.defold.extension.siwa
|
||||||
|
|
4
input/game.input_binding
Executable file
4
input/game.input_binding
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
mouse_trigger {
|
||||||
|
input: MOUSE_BUTTON_1
|
||||||
|
action: "touch"
|
||||||
|
}
|
37
main/main.collection
Executable file
37
main/main.collection
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
name: "default"
|
||||||
|
scale_along_z: 0
|
||||||
|
embedded_instances {
|
||||||
|
id: "main_go"
|
||||||
|
data: "components {\n"
|
||||||
|
" id: \"main\"\n"
|
||||||
|
" component: \"/main/main.gui\"\n"
|
||||||
|
" position {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" }\n"
|
||||||
|
" rotation {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" w: 1.0\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
""
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale3 {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
}
|
||||||
|
}
|
551
main/main.gui
Executable file
551
main/main.gui
Executable file
@ -0,0 +1,551 @@
|
|||||||
|
script: "/main/main.gui_script"
|
||||||
|
fonts {
|
||||||
|
name: "system_font"
|
||||||
|
font: "/builtins/fonts/system_font.font"
|
||||||
|
}
|
||||||
|
background_color {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 114.0
|
||||||
|
y: 418.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 0.5
|
||||||
|
y: 0.5
|
||||||
|
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_TEMPLATE
|
||||||
|
id: "login"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
template: "/dirtylarry/button.gui"
|
||||||
|
template_node_child: false
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.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: 300.0
|
||||||
|
y: 88.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: "button/button_normal"
|
||||||
|
id: "login/larrybutton"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
parent: "login"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 32.0
|
||||||
|
y: 32.0
|
||||||
|
z: 32.0
|
||||||
|
w: 32.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: true
|
||||||
|
size_mode: SIZE_MODE_MANUAL
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.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: "Login"
|
||||||
|
font: "larryfont"
|
||||||
|
id: "login/larrylabel"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
outline {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.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: "login/larrybutton"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
outline_alpha: 1.0
|
||||||
|
shadow_alpha: 1.0
|
||||||
|
overridden_fields: 8
|
||||||
|
template_node_child: true
|
||||||
|
text_leading: 1.0
|
||||||
|
text_tracking: 0.0
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 114.0
|
||||||
|
y: 333.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 0.5
|
||||||
|
y: 0.5
|
||||||
|
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_TEMPLATE
|
||||||
|
id: "check"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
template: "/dirtylarry/button.gui"
|
||||||
|
template_node_child: false
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.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: 300.0
|
||||||
|
y: 88.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: "button/button_normal"
|
||||||
|
id: "check/larrybutton"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
parent: "check"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 32.0
|
||||||
|
y: 32.0
|
||||||
|
z: 32.0
|
||||||
|
w: 32.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: true
|
||||||
|
size_mode: SIZE_MODE_MANUAL
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.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: "Check"
|
||||||
|
font: "larryfont"
|
||||||
|
id: "check/larrylabel"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
outline {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.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: "check/larrybutton"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
outline_alpha: 1.0
|
||||||
|
shadow_alpha: 1.0
|
||||||
|
overridden_fields: 8
|
||||||
|
template_node_child: true
|
||||||
|
text_leading: 1.0
|
||||||
|
text_tracking: 0.0
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 114.0
|
||||||
|
y: 255.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 0.5
|
||||||
|
y: 0.5
|
||||||
|
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_TEMPLATE
|
||||||
|
id: "check_fail"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
template: "/dirtylarry/button.gui"
|
||||||
|
template_node_child: false
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.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: 300.0
|
||||||
|
y: 88.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: "button/button_normal"
|
||||||
|
id: "check_fail/larrybutton"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
parent: "check_fail"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 32.0
|
||||||
|
y: 32.0
|
||||||
|
z: 32.0
|
||||||
|
w: 32.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: true
|
||||||
|
size_mode: SIZE_MODE_MANUAL
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.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: "Check\n"
|
||||||
|
"Fail"
|
||||||
|
font: "larryfont"
|
||||||
|
id: "check_fail/larrylabel"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
outline {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.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: "check_fail/larrybutton"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
outline_alpha: 1.0
|
||||||
|
shadow_alpha: 1.0
|
||||||
|
overridden_fields: 8
|
||||||
|
template_node_child: true
|
||||||
|
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
|
64
main/main.gui_script
Executable file
64
main/main.gui_script
Executable file
@ -0,0 +1,64 @@
|
|||||||
|
local dl = require("dirtylarry.dirtylarry")
|
||||||
|
|
||||||
|
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 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
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function authenticate(self)
|
||||||
|
log("authenticate")
|
||||||
|
siwa.authenticate(function(self, data)
|
||||||
|
self.user_id = data.user_id
|
||||||
|
for k,v in pairs(data) do
|
||||||
|
log("%s: %s", k, tostring(v))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_siwa_supported()
|
||||||
|
return siwa and siwa.is_supported()
|
||||||
|
end
|
||||||
|
|
||||||
|
function init(self)
|
||||||
|
msg.post(".", "acquire_input_focus")
|
||||||
|
|
||||||
|
if is_siwa_supported() then
|
||||||
|
log("SIWA supported!")
|
||||||
|
else
|
||||||
|
log("SIWA not supported...")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_input(self, action_id, action)
|
||||||
|
if is_siwa_supported() then
|
||||||
|
dl:button("check", action_id, action, function()
|
||||||
|
if self.user_id then
|
||||||
|
get_credential_state(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, "foobar")
|
||||||
|
end)
|
||||||
|
|
||||||
|
dl:button("login", action_id, action, function()
|
||||||
|
authenticate(self)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
115
siwa/api/siwa.script_api
Normal file
115
siwa/api/siwa.script_api
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
- 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 (user_id, credential_state)
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- desc: |-
|
||||||
|
```lua
|
||||||
|
siwa.get_credential_state(id, function(self, data)
|
||||||
|
if data.credential_state == siwa.STATE_AUTHORIZED then
|
||||||
|
print("User has still authorized the application", data.user_id)
|
||||||
|
elseif data.credential_state == siwa.STATE_REVOKED then
|
||||||
|
print("User has revoked authorization for the application", data.user_id)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- 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 (user_id, identity_token, email, first_name, family_name, status, result)
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- desc: |-
|
||||||
|
```lua
|
||||||
|
siwa.authenticate(function(self, data)
|
||||||
|
print(data.identity_token)
|
||||||
|
print(data.user_id)
|
||||||
|
print(data.first_name, data.family_name)
|
||||||
|
print(data.email)
|
||||||
|
if data.user_status == siwa.STATUS_LIKELY_REAL then
|
||||||
|
print("Likely a real person")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
#*****************************************************************************************************
|
||||||
|
|
||||||
|
- name: STATE_NOT_FOUND
|
||||||
|
type: number
|
||||||
|
desc: The user can’t be found.
|
||||||
|
|
||||||
|
|
||||||
|
- name: STATE_UNKNOWN
|
||||||
|
type: number
|
||||||
|
desc: Unknown credential state.
|
||||||
|
|
||||||
|
|
||||||
|
- name: STATE_AUTHORIZED
|
||||||
|
type: number
|
||||||
|
desc: The user is authorized.
|
||||||
|
|
||||||
|
|
||||||
|
- name: STATE_REVOKED
|
||||||
|
type: number
|
||||||
|
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.
|
30
siwa/ext.manifest
Executable file
30
siwa/ext.manifest
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
name: siwa
|
||||||
|
|
||||||
|
platforms:
|
||||||
|
x86-osx:
|
||||||
|
context:
|
||||||
|
frameworks: []
|
||||||
|
flags: ["-std=c++11", "-stdlib=libc++"]
|
||||||
|
linkFlags: ["-stdlib=libc++"]
|
||||||
|
libs: ["c++"]
|
||||||
|
|
||||||
|
x86_64-osx:
|
||||||
|
context:
|
||||||
|
frameworks: []
|
||||||
|
flags: ["-std=c++11", "-stdlib=libc++"]
|
||||||
|
libs: ["c++"]
|
||||||
|
|
||||||
|
x86_64-ios:
|
||||||
|
context:
|
||||||
|
frameworks: ['AuthenticationServices']
|
||||||
|
defines: ['SUPPORTS_SIWA']
|
||||||
|
|
||||||
|
armv7-ios:
|
||||||
|
context:
|
||||||
|
frameworks: ['AuthenticationServices']
|
||||||
|
defines: ['SUPPORTS_SIWA']
|
||||||
|
|
||||||
|
arm64-ios:
|
||||||
|
context:
|
||||||
|
frameworks: ['AuthenticationServices']
|
||||||
|
defines: ['SUPPORTS_SIWA']
|
292
siwa/src/siwa.cpp
Executable file
292
siwa/src/siwa.cpp
Executable file
@ -0,0 +1,292 @@
|
|||||||
|
#if defined(DM_PLATFORM_IOS)
|
||||||
|
|
||||||
|
#include "siwa.h"
|
||||||
|
#include <dmsdk/sdk.h>
|
||||||
|
|
||||||
|
#define MODULE_NAME "siwa"
|
||||||
|
|
||||||
|
SiwaData g_SiwaData;
|
||||||
|
SiwaCallbackData g_SiwaCallbackData;
|
||||||
|
|
||||||
|
char* Siwa_GetUserId()
|
||||||
|
{
|
||||||
|
return g_SiwaData.m_userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Siwa_ResetCallbackData()
|
||||||
|
{
|
||||||
|
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 = STATUS_UNSUPPORTED;
|
||||||
|
g_SiwaCallbackData.m_state = STATE_UNKNOWN;
|
||||||
|
g_SiwaCallbackData.m_cmd = CMD_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_SiwaCallbackData.m_cmd = CMD_CREDENTIAL;
|
||||||
|
g_SiwaCallbackData.m_userID = strdup(userID);
|
||||||
|
g_SiwaCallbackData.m_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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!");
|
||||||
|
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 Siwa_QueueAuthFailureCallback(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Siwa_TriggerCallback()
|
||||||
|
{
|
||||||
|
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, "credential_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Siwa_SetupCallback(lua_State* L, int index)
|
||||||
|
{
|
||||||
|
if (g_SiwaData.m_callback) {
|
||||||
|
dmScript::DestroyCallback(g_SiwaData.m_callback);
|
||||||
|
}
|
||||||
|
g_SiwaData.m_callback = dmScript::CreateCallback(L, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Siwa_CleanupCallback() {
|
||||||
|
if (g_SiwaData.m_callback) {
|
||||||
|
dmScript::DestroyCallback(g_SiwaData.m_callback);
|
||||||
|
g_SiwaData.m_callback = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Siwa_GetCredentialState(lua_State* L){
|
||||||
|
DM_LUA_STACK_CHECK(L, 1);
|
||||||
|
|
||||||
|
if (!Siwa_PlatformIsSupported()) {
|
||||||
|
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_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);
|
||||||
|
Siwa_SetupCallback(L, 2);
|
||||||
|
Siwa_PlatformGetCredentialState();
|
||||||
|
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Siwa_AuthenticateWithApple(lua_State* L) {
|
||||||
|
DM_LUA_STACK_CHECK(L, 1);
|
||||||
|
|
||||||
|
if (!Siwa_PlatformIsSupported()) {
|
||||||
|
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);
|
||||||
|
Siwa_SetupCallback(L, 1);
|
||||||
|
Siwa_PlatformAuthenticateWithApple();
|
||||||
|
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Siwa_IsSupported(lua_State* L) {
|
||||||
|
DM_LUA_STACK_CHECK(L, 1);
|
||||||
|
lua_pushboolean(L, Siwa_PlatformIsSupported());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dmExtension::Result SiwaAppInitialize(dmExtension::AppParams* params)
|
||||||
|
{
|
||||||
|
Siwa_ResetCallbackData();
|
||||||
|
return dmExtension::RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dmExtension::Result SiwaAppFinalize(dmExtension::AppParams* params)
|
||||||
|
{
|
||||||
|
return dmExtension::RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const luaL_reg lua_register[] =
|
||||||
|
{
|
||||||
|
{"is_supported", Siwa_IsSupported},
|
||||||
|
{"get_credential_state", Siwa_GetCredentialState},
|
||||||
|
{"authenticate", Siwa_AuthenticateWithApple},
|
||||||
|
{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)
|
||||||
|
|
||||||
|
SETCONSTANT(STATUS_UNKNOWN)
|
||||||
|
SETCONSTANT(STATUS_UNSUPPORTED)
|
||||||
|
SETCONSTANT(STATUS_LIKELY_REAL)
|
||||||
|
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
Siwa_TriggerCallback();
|
||||||
|
Siwa_ResetCallbackData();
|
||||||
|
Siwa_CleanupCallback();
|
||||||
|
}
|
||||||
|
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
|
86
siwa/src/siwa.h
Executable file
86
siwa/src/siwa.h
Executable file
@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
#if defined(DM_PLATFORM_IOS)
|
||||||
|
|
||||||
|
#include <dmsdk/sdk.h>
|
||||||
|
|
||||||
|
enum SiwaCallbackCmd
|
||||||
|
{
|
||||||
|
CMD_NONE = 0,
|
||||||
|
CMD_CREDENTIAL = 1,
|
||||||
|
CMD_AUTH_SUCCESS = 2,
|
||||||
|
CMD_AUTH_FAILED = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://developer.apple.com/documentation/authenticationservices/asauthorizationappleidprovidercredentialstate/asauthorizationappleidprovidercredentialauthorized?language=objc
|
||||||
|
enum SiwaCredentialState
|
||||||
|
{
|
||||||
|
STATE_UNKNOWN = 0,
|
||||||
|
STATE_AUTHORIZED = 1,
|
||||||
|
STATE_REVOKED = 2,
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
memset(this, 0, sizeof(*this));
|
||||||
|
};
|
||||||
|
|
||||||
|
SiwaCallbackCmd m_cmd;
|
||||||
|
SiwaCredentialState m_state;
|
||||||
|
SiwaUserDetectionStatus m_userStatus;
|
||||||
|
|
||||||
|
char* m_identityToken;
|
||||||
|
char* m_userID;
|
||||||
|
char* m_email;
|
||||||
|
char* m_firstName;
|
||||||
|
char* m_familyName;
|
||||||
|
|
||||||
|
char* m_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SiwaData
|
||||||
|
{
|
||||||
|
SiwaData()
|
||||||
|
{
|
||||||
|
memset(this, 0, sizeof(*this));
|
||||||
|
};
|
||||||
|
|
||||||
|
// the user ID used for checking credential state
|
||||||
|
char* m_userID;
|
||||||
|
|
||||||
|
dmScript::LuaCallbackInfo* m_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
char* Siwa_GetUserId();
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Trigged by a call from lua to check if sign in with apple is supported on this device.
|
||||||
|
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 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 Siwa_PlatformGetCredentialState();
|
||||||
|
|
||||||
|
#endif
|
184
siwa/src/siwa_ios.mm
Executable file
184
siwa/src/siwa_ios.mm
Executable file
@ -0,0 +1,184 @@
|
|||||||
|
#if defined(DM_PLATFORM_IOS)
|
||||||
|
|
||||||
|
#include "siwa.h"
|
||||||
|
|
||||||
|
#include <AuthenticationServices/AuthenticationServices.h>
|
||||||
|
|
||||||
|
#include <dmsdk/sdk.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// This class is that delegate.
|
||||||
|
// It also owns the provider that is both used for the sign in flow, as well as for credential state checking.
|
||||||
|
API_AVAILABLE(ios(13.0))
|
||||||
|
@interface SiwaManager : NSObject <ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding>
|
||||||
|
@property (nonatomic, strong) ASAuthorizationAppleIDProvider *m_idProvider;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SiwaManager
|
||||||
|
|
||||||
|
- (instancetype) init
|
||||||
|
{
|
||||||
|
self = [super init];
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
self.m_idProvider = [[ASAuthorizationAppleIDProvider alloc] init];
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user id provided to use from lua still grants our app permission
|
||||||
|
// to use it for sign in. User's can revoke app's permissions to use the id for sign in
|
||||||
|
// at any time, so we need to be able to monitor this.
|
||||||
|
// 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) getCredentialState
|
||||||
|
{
|
||||||
|
char* userId = Siwa_GetUserId();
|
||||||
|
NSString* user_id_string = [[NSString alloc] initWithUTF8String:userId];
|
||||||
|
|
||||||
|
[self.m_idProvider getCredentialStateForUserID: user_id_string
|
||||||
|
completion: ^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError* error) {
|
||||||
|
|
||||||
|
// TODO: docs provide no information about what type of errors we can expect:
|
||||||
|
// https://developer.apple.com/documentation/authenticationservices/asauthorizationappleidprovider/3175423-getcredentialstateforuserid?language=objc
|
||||||
|
if (error) {
|
||||||
|
NSString *errorMessage = [NSString stringWithFormat: @"getCredentialStateForUserID completed with error: %@", [error localizedDescription]];
|
||||||
|
dmLogError([errorMessage UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SiwaCredentialState state = STATE_UNKNOWN;
|
||||||
|
switch(credentialState) {
|
||||||
|
case 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Siwa_QueueCredentialCallback(userId, state);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// triggers the sign in with Apple native ui flow to begin.
|
||||||
|
- (void) loginWithUI
|
||||||
|
{
|
||||||
|
ASAuthorizationAppleIDRequest* request = [self.m_idProvider createRequest];
|
||||||
|
request.requestedScopes = @[ASAuthorizationScopeFullName, ASAuthorizationScopeEmail];
|
||||||
|
|
||||||
|
ASAuthorizationController* authController = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]];
|
||||||
|
authController.presentationContextProvider = self;
|
||||||
|
authController.delegate = self;
|
||||||
|
|
||||||
|
[authController performRequests];
|
||||||
|
}
|
||||||
|
|
||||||
|
// the Auth controller needs to specify where to display the native login.
|
||||||
|
// this is the function our SiwaManager delegate has to implement to provide that information.
|
||||||
|
- (ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller {
|
||||||
|
UIWindow *window = [UIApplication sharedApplication].keyWindow;
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the Auth controller callback for getting a response back from apple for a sign in
|
||||||
|
- (void)authorizationController:(ASAuthorizationController *)controller
|
||||||
|
didCompleteWithAuthorization:(ASAuthorization *)authorization {
|
||||||
|
if ([authorization.credential class] == [ASAuthorizationAppleIDCredential class]) {
|
||||||
|
ASAuthorizationAppleIDCredential* appleIdCredential = ((ASAuthorizationAppleIDCredential*) authorization.credential);
|
||||||
|
|
||||||
|
const char* appleUserId = [appleIdCredential.user UTF8String];
|
||||||
|
const char* email = [appleIdCredential.email UTF8String];
|
||||||
|
const char* givenName = [appleIdCredential.fullName.givenName UTF8String];
|
||||||
|
const char* familyName = [appleIdCredential.fullName.familyName UTF8String];
|
||||||
|
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];
|
||||||
|
|
||||||
|
Siwa_QueueAuthSuccessCallback(identityToken, appleUserId, email, givenName, familyName, userDetectionStatus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Siwa_QueueAuthFailureCallback("authorization failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Auth controller callback for getting an error during authorization
|
||||||
|
- (void)authorizationController:(ASAuthorizationController *)controller
|
||||||
|
didCompleteWithError:(NSError *)error {
|
||||||
|
NSString *errorMessage = [NSString stringWithFormat: @"Authorization error: %@", [error localizedDescription]];
|
||||||
|
Siwa_QueueAuthFailureCallback([errorMessage UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
API_AVAILABLE(ios(13.0))
|
||||||
|
static SiwaManager* g_SiwaManager = nil;
|
||||||
|
|
||||||
|
API_AVAILABLE(ios(13.0))
|
||||||
|
SiwaManager* GetSiwaManager()
|
||||||
|
{
|
||||||
|
if(g_SiwaManager == nil)
|
||||||
|
{
|
||||||
|
g_SiwaManager = [[SiwaManager alloc] init];
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_SiwaManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
API_AVAILABLE(ios(13.0))
|
||||||
|
// Kicks off the request to get the credential state of a provided user id.
|
||||||
|
void Siwa_PlatformDoGetCredentialState() {
|
||||||
|
SiwaManager *siwaMan = GetSiwaManager();
|
||||||
|
[siwaMan getCredentialState];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
API_AVAILABLE(ios(13.0))
|
||||||
|
// Kicks off the sign in with apple flow.
|
||||||
|
void Siwa_PlatformDoAuthenticateWithApple() {
|
||||||
|
SiwaManager *siwaMan = GetSiwaManager();
|
||||||
|
[siwaMan loginWithUI];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 Siwa_PlatformAuthenticateWithApple() {
|
||||||
|
Siwa_PlatformDoAuthenticateWithApple();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
6
siwa/src/siwa_null.cpp
Executable file
6
siwa/src/siwa_null.cpp
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#if !defined(DM_PLATFORM_IOS)
|
||||||
|
extern "C" void siwa()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user