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