@capawesome/capacitor-apple-sign-in 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapawesomeCapacitorAppleSignIn.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +220 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/AppleSignIn.java +98 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/AppleSignInActivity.java +366 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/AppleSignInPlugin.java +106 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/classes/CustomException.java +20 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/classes/CustomExceptions.java +12 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/classes/options/SignInOptions.java +84 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/classes/results/SignInResult.java +65 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/interfaces/Callback.java +5 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/interfaces/NonEmptyResultCallback.java +7 -0
- package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/interfaces/Result.java +7 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/android/src/main/res/drawable/ic_close.xml +9 -0
- package/dist/docs.json +345 -0
- package/dist/esm/definitions.d.ts +181 -0
- package/dist/esm/definitions.js +55 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +10 -0
- package/dist/esm/web.js +79 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +148 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +151 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/AppleSignIn.swift +48 -0
- package/ios/Plugin/AppleSignInPlugin.swift +67 -0
- package/ios/Plugin/Classes/Options/SignInOptions.swift +31 -0
- package/ios/Plugin/Classes/Results/SignInResult.swift +46 -0
- package/ios/Plugin/Enums/CustomError.swift +26 -0
- package/ios/Plugin/Info.plist +24 -0
- package/ios/Plugin/Protocols/Result.swift +6 -0
- package/package.json +93 -0
package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/AppleSignInActivity.java
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.applesignin;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.Intent;
|
|
5
|
+
import android.graphics.Bitmap;
|
|
6
|
+
import android.graphics.PorterDuff;
|
|
7
|
+
import android.net.Uri;
|
|
8
|
+
import android.os.Build;
|
|
9
|
+
import android.os.Bundle;
|
|
10
|
+
import android.text.TextUtils;
|
|
11
|
+
import android.util.Log;
|
|
12
|
+
import android.util.TypedValue;
|
|
13
|
+
import android.view.Gravity;
|
|
14
|
+
import android.view.View;
|
|
15
|
+
import android.view.ViewGroup;
|
|
16
|
+
import android.view.Window;
|
|
17
|
+
import android.webkit.CookieManager;
|
|
18
|
+
import android.webkit.JavascriptInterface;
|
|
19
|
+
import android.webkit.WebChromeClient;
|
|
20
|
+
import android.webkit.WebResourceRequest;
|
|
21
|
+
import android.webkit.WebResourceResponse;
|
|
22
|
+
import android.webkit.WebSettings;
|
|
23
|
+
import android.webkit.WebView;
|
|
24
|
+
import android.webkit.WebViewClient;
|
|
25
|
+
import android.widget.ImageButton;
|
|
26
|
+
import android.widget.LinearLayout;
|
|
27
|
+
import android.widget.ProgressBar;
|
|
28
|
+
import android.widget.RelativeLayout;
|
|
29
|
+
import android.widget.TextView;
|
|
30
|
+
import androidx.annotation.NonNull;
|
|
31
|
+
import androidx.core.graphics.Insets;
|
|
32
|
+
import androidx.core.view.ViewCompat;
|
|
33
|
+
import androidx.core.view.WindowInsetsCompat;
|
|
34
|
+
import java.io.InputStream;
|
|
35
|
+
import java.net.HttpURLConnection;
|
|
36
|
+
import java.net.URL;
|
|
37
|
+
import org.json.JSONObject;
|
|
38
|
+
|
|
39
|
+
public class AppleSignInActivity extends Activity {
|
|
40
|
+
|
|
41
|
+
public static final String EXTRA_URL = "url";
|
|
42
|
+
public static final String EXTRA_REDIRECT_URL = "redirectUrl";
|
|
43
|
+
public static final String EXTRA_AUTHORIZATION_CODE = "authorizationCode";
|
|
44
|
+
public static final String EXTRA_ID_TOKEN = "idToken";
|
|
45
|
+
public static final String EXTRA_USER = "user";
|
|
46
|
+
public static final String EXTRA_EMAIL = "email";
|
|
47
|
+
public static final String EXTRA_GIVEN_NAME = "givenName";
|
|
48
|
+
public static final String EXTRA_FAMILY_NAME = "familyName";
|
|
49
|
+
public static final String EXTRA_STATE = "state";
|
|
50
|
+
|
|
51
|
+
private String redirectUrl;
|
|
52
|
+
private TextView domainTextView;
|
|
53
|
+
private ProgressBar progressBar;
|
|
54
|
+
|
|
55
|
+
@Override
|
|
56
|
+
protected void onCreate(Bundle savedInstanceState) {
|
|
57
|
+
super.onCreate(savedInstanceState);
|
|
58
|
+
|
|
59
|
+
String url = getIntent().getStringExtra(EXTRA_URL);
|
|
60
|
+
redirectUrl = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
|
|
61
|
+
|
|
62
|
+
LinearLayout rootLayout = new LinearLayout(this);
|
|
63
|
+
rootLayout.setOrientation(LinearLayout.VERTICAL);
|
|
64
|
+
rootLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
65
|
+
|
|
66
|
+
// Toolbar
|
|
67
|
+
RelativeLayout toolbar = new RelativeLayout(this);
|
|
68
|
+
toolbar.setBackgroundColor(0xFFF8F8F8);
|
|
69
|
+
LinearLayout.LayoutParams toolbarParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(56));
|
|
70
|
+
toolbar.setLayoutParams(toolbarParams);
|
|
71
|
+
|
|
72
|
+
ImageButton closeButton = new ImageButton(this);
|
|
73
|
+
closeButton.setImageResource(R.drawable.ic_close);
|
|
74
|
+
closeButton.setBackgroundColor(0x00000000);
|
|
75
|
+
int closePadding = dpToPx(12);
|
|
76
|
+
closeButton.setPadding(closePadding, closePadding, closePadding, closePadding);
|
|
77
|
+
closeButton.setContentDescription("Close");
|
|
78
|
+
closeButton.setOnClickListener(v -> {
|
|
79
|
+
setResult(RESULT_CANCELED);
|
|
80
|
+
finish();
|
|
81
|
+
});
|
|
82
|
+
RelativeLayout.LayoutParams closeParams = new RelativeLayout.LayoutParams(dpToPx(48), dpToPx(48));
|
|
83
|
+
closeParams.addRule(RelativeLayout.ALIGN_PARENT_START);
|
|
84
|
+
closeParams.addRule(RelativeLayout.CENTER_VERTICAL);
|
|
85
|
+
closeParams.setMarginStart(dpToPx(4));
|
|
86
|
+
|
|
87
|
+
domainTextView = new TextView(this);
|
|
88
|
+
domainTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
|
|
89
|
+
domainTextView.setTextColor(0xFF5F6368);
|
|
90
|
+
domainTextView.setSingleLine(true);
|
|
91
|
+
domainTextView.setEllipsize(TextUtils.TruncateAt.END);
|
|
92
|
+
domainTextView.setGravity(Gravity.CENTER);
|
|
93
|
+
RelativeLayout.LayoutParams domainParams = new RelativeLayout.LayoutParams(
|
|
94
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
95
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
96
|
+
);
|
|
97
|
+
domainParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
|
98
|
+
int domainHorizontalMargin = dpToPx(56);
|
|
99
|
+
domainParams.setMarginStart(domainHorizontalMargin);
|
|
100
|
+
domainParams.setMarginEnd(domainHorizontalMargin);
|
|
101
|
+
|
|
102
|
+
toolbar.addView(closeButton, closeParams);
|
|
103
|
+
toolbar.addView(domainTextView, domainParams);
|
|
104
|
+
|
|
105
|
+
// Progress bar
|
|
106
|
+
progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
|
|
107
|
+
progressBar.setIndeterminate(false);
|
|
108
|
+
progressBar.setMax(100);
|
|
109
|
+
progressBar.setProgress(0);
|
|
110
|
+
progressBar.getProgressDrawable().setColorFilter(0xFF007AFF, PorterDuff.Mode.SRC_IN);
|
|
111
|
+
progressBar.setVisibility(View.GONE);
|
|
112
|
+
LinearLayout.LayoutParams progressParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(3));
|
|
113
|
+
|
|
114
|
+
// WebView
|
|
115
|
+
WebView webView = new WebView(this);
|
|
116
|
+
LinearLayout.LayoutParams webViewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f);
|
|
117
|
+
|
|
118
|
+
WebSettings webSettings = webView.getSettings();
|
|
119
|
+
webSettings.setJavaScriptEnabled(true);
|
|
120
|
+
webSettings.setDomStorageEnabled(true);
|
|
121
|
+
String userAgent = webSettings.getUserAgentString().replaceAll("; wv\\b", "").replaceAll("\\s*Version/\\S+", "");
|
|
122
|
+
webSettings.setUserAgentString(userAgent);
|
|
123
|
+
Log.d("AppleSignIn", "User-Agent: " + userAgent);
|
|
124
|
+
Log.d("AppleSignIn", "Loading URL: " + url);
|
|
125
|
+
|
|
126
|
+
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
|
|
127
|
+
|
|
128
|
+
webView.addJavascriptInterface(new AppleSignInBridge(), "AppleSignInBridge");
|
|
129
|
+
|
|
130
|
+
webView.setWebChromeClient(
|
|
131
|
+
new WebChromeClient() {
|
|
132
|
+
@Override
|
|
133
|
+
public void onProgressChanged(WebView view, int newProgress) {
|
|
134
|
+
progressBar.setProgress(newProgress);
|
|
135
|
+
progressBar.setVisibility(newProgress < 100 ? View.VISIBLE : View.GONE);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
webView.setWebViewClient(
|
|
140
|
+
new WebViewClient() {
|
|
141
|
+
@Override
|
|
142
|
+
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
|
|
143
|
+
String requestUrl = request.getUrl().toString();
|
|
144
|
+
if (!requestUrl.startsWith("https://appleid.apple.com")) {
|
|
145
|
+
return super.shouldInterceptRequest(view, request);
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
HttpURLConnection conn = (HttpURLConnection) new URL(requestUrl).openConnection();
|
|
149
|
+
conn.setRequestMethod(request.getMethod());
|
|
150
|
+
for (java.util.Map.Entry<String, String> entry : request.getRequestHeaders().entrySet()) {
|
|
151
|
+
if (!entry.getKey().equalsIgnoreCase("X-Requested-With")) {
|
|
152
|
+
conn.setRequestProperty(entry.getKey(), entry.getValue());
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
conn.setInstanceFollowRedirects(false);
|
|
156
|
+
int statusCode = conn.getResponseCode();
|
|
157
|
+
String reasonPhrase = conn.getResponseMessage();
|
|
158
|
+
InputStream inputStream = (statusCode >= 400) ? conn.getErrorStream() : conn.getInputStream();
|
|
159
|
+
String contentType = conn.getContentType();
|
|
160
|
+
String mimeType = "text/html";
|
|
161
|
+
String encoding = "utf-8";
|
|
162
|
+
if (contentType != null) {
|
|
163
|
+
String[] parts = contentType.split(";");
|
|
164
|
+
mimeType = parts[0].trim();
|
|
165
|
+
for (String part : parts) {
|
|
166
|
+
String trimmed = part.trim();
|
|
167
|
+
if (trimmed.startsWith("charset=")) {
|
|
168
|
+
encoding = trimmed.substring(8).trim();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
java.util.Map<String, String> responseHeaders = new java.util.HashMap<>();
|
|
173
|
+
for (java.util.Map.Entry<String, java.util.List<String>> header : conn.getHeaderFields().entrySet()) {
|
|
174
|
+
if (header.getKey() != null && !header.getValue().isEmpty()) {
|
|
175
|
+
responseHeaders.put(header.getKey(), header.getValue().get(0));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return new WebResourceResponse(
|
|
179
|
+
mimeType,
|
|
180
|
+
encoding,
|
|
181
|
+
statusCode,
|
|
182
|
+
reasonPhrase != null ? reasonPhrase : "OK",
|
|
183
|
+
responseHeaders,
|
|
184
|
+
inputStream
|
|
185
|
+
);
|
|
186
|
+
} catch (Exception e) {
|
|
187
|
+
Log.e("AppleSignIn", "Intercept failed: " + e.getMessage());
|
|
188
|
+
return super.shouldInterceptRequest(view, request);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Override
|
|
193
|
+
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
|
|
194
|
+
super.onReceivedHttpError(view, request, errorResponse);
|
|
195
|
+
Log.e(
|
|
196
|
+
"AppleSignIn",
|
|
197
|
+
"HTTP error " +
|
|
198
|
+
errorResponse.getStatusCode() +
|
|
199
|
+
" for URL: " +
|
|
200
|
+
request.getUrl() +
|
|
201
|
+
" | Reason: " +
|
|
202
|
+
errorResponse.getReasonPhrase()
|
|
203
|
+
);
|
|
204
|
+
java.util.Map<String, String> headers = errorResponse.getResponseHeaders();
|
|
205
|
+
if (headers != null) {
|
|
206
|
+
for (java.util.Map.Entry<String, String> entry : headers.entrySet()) {
|
|
207
|
+
Log.d("AppleSignIn", "Response header: " + entry.getKey() + " = " + entry.getValue());
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@Override
|
|
213
|
+
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
|
214
|
+
super.onPageStarted(view, url, favicon);
|
|
215
|
+
updateDomainText(url);
|
|
216
|
+
String js =
|
|
217
|
+
"(function() {" +
|
|
218
|
+
" var origSubmit = HTMLFormElement.prototype.submit;" +
|
|
219
|
+
" HTMLFormElement.prototype.submit = function() {" +
|
|
220
|
+
" if (this.action && this.action.indexOf('" +
|
|
221
|
+
escapeJsString(redirectUrl) +
|
|
222
|
+
"') !== -1) {" +
|
|
223
|
+
" var data = {};" +
|
|
224
|
+
" for (var i = 0; i < this.elements.length; i++) {" +
|
|
225
|
+
" if (this.elements[i].name) {" +
|
|
226
|
+
" data[this.elements[i].name] = this.elements[i].value;" +
|
|
227
|
+
" }" +
|
|
228
|
+
" }" +
|
|
229
|
+
" window.AppleSignInBridge.onFormData(JSON.stringify(data));" +
|
|
230
|
+
" return;" +
|
|
231
|
+
" }" +
|
|
232
|
+
" origSubmit.call(this);" +
|
|
233
|
+
" };" +
|
|
234
|
+
"})();";
|
|
235
|
+
view.evaluateJavascript(js, null);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
@Override
|
|
239
|
+
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
|
240
|
+
String requestUrl = request.getUrl().toString();
|
|
241
|
+
if (requestUrl.startsWith(redirectUrl)) {
|
|
242
|
+
handleRedirectUrl(requestUrl);
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
return super.shouldOverrideUrlLoading(view, request);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
rootLayout.addView(toolbar);
|
|
251
|
+
rootLayout.addView(progressBar, progressParams);
|
|
252
|
+
rootLayout.addView(webView, webViewParams);
|
|
253
|
+
setContentView(rootLayout);
|
|
254
|
+
setupStatusBar();
|
|
255
|
+
|
|
256
|
+
ViewCompat.setOnApplyWindowInsetsListener(rootLayout, (v, windowInsets) -> {
|
|
257
|
+
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars());
|
|
258
|
+
v.setPadding(0, insets.top, 0, 0);
|
|
259
|
+
return WindowInsetsCompat.CONSUMED;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
webView.loadUrl(url);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private void handleRedirectUrl(@NonNull String url) {
|
|
266
|
+
Uri uri = Uri.parse(url);
|
|
267
|
+
String code = uri.getQueryParameter("code");
|
|
268
|
+
String idToken = uri.getQueryParameter("id_token");
|
|
269
|
+
String state = uri.getQueryParameter("state");
|
|
270
|
+
|
|
271
|
+
if (code != null && idToken != null) {
|
|
272
|
+
Intent resultIntent = new Intent();
|
|
273
|
+
resultIntent.putExtra(EXTRA_AUTHORIZATION_CODE, code);
|
|
274
|
+
resultIntent.putExtra(EXTRA_ID_TOKEN, idToken);
|
|
275
|
+
resultIntent.putExtra(EXTRA_STATE, state);
|
|
276
|
+
setResult(RESULT_OK, resultIntent);
|
|
277
|
+
} else {
|
|
278
|
+
setResult(RESULT_CANCELED);
|
|
279
|
+
}
|
|
280
|
+
finish();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private int dpToPx(int dp) {
|
|
284
|
+
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@NonNull
|
|
288
|
+
private static String escapeJsString(@NonNull String input) {
|
|
289
|
+
return input.replace("\\", "\\\\").replace("'", "\\'").replace("\"", "\\\"");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@SuppressWarnings("deprecation")
|
|
293
|
+
private void setupStatusBar() {
|
|
294
|
+
Window window = getWindow();
|
|
295
|
+
window.setStatusBarColor(0xFFF8F8F8);
|
|
296
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
297
|
+
android.view.WindowInsetsController controller = window.getInsetsController();
|
|
298
|
+
if (controller != null) {
|
|
299
|
+
controller.setSystemBarsAppearance(
|
|
300
|
+
android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
|
|
301
|
+
android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private void updateDomainText(@NonNull String url) {
|
|
310
|
+
try {
|
|
311
|
+
String host = Uri.parse(url).getHost();
|
|
312
|
+
if (host != null) {
|
|
313
|
+
domainTextView.setText(host);
|
|
314
|
+
}
|
|
315
|
+
} catch (Exception e) {
|
|
316
|
+
// ignore
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private class AppleSignInBridge {
|
|
321
|
+
|
|
322
|
+
@JavascriptInterface
|
|
323
|
+
public void onFormData(String jsonString) {
|
|
324
|
+
try {
|
|
325
|
+
JSONObject data = new JSONObject(jsonString);
|
|
326
|
+
|
|
327
|
+
String code = data.optString("code", null);
|
|
328
|
+
String idToken = data.optString("id_token", null);
|
|
329
|
+
String state = data.optString("state", null);
|
|
330
|
+
String userJson = data.optString("user", null);
|
|
331
|
+
|
|
332
|
+
String email = null;
|
|
333
|
+
String givenName = null;
|
|
334
|
+
String familyName = null;
|
|
335
|
+
|
|
336
|
+
if (userJson != null && !userJson.isEmpty()) {
|
|
337
|
+
try {
|
|
338
|
+
JSONObject userObj = new JSONObject(userJson);
|
|
339
|
+
email = userObj.optString("email", null);
|
|
340
|
+
JSONObject nameObj = userObj.optJSONObject("name");
|
|
341
|
+
if (nameObj != null) {
|
|
342
|
+
givenName = nameObj.optString("firstName", null);
|
|
343
|
+
familyName = nameObj.optString("lastName", null);
|
|
344
|
+
}
|
|
345
|
+
} catch (Exception e) {
|
|
346
|
+
// ignore
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
Intent resultIntent = new Intent();
|
|
351
|
+
resultIntent.putExtra(EXTRA_AUTHORIZATION_CODE, code);
|
|
352
|
+
resultIntent.putExtra(EXTRA_ID_TOKEN, idToken);
|
|
353
|
+
resultIntent.putExtra(EXTRA_STATE, state);
|
|
354
|
+
resultIntent.putExtra(EXTRA_EMAIL, email);
|
|
355
|
+
resultIntent.putExtra(EXTRA_GIVEN_NAME, givenName);
|
|
356
|
+
resultIntent.putExtra(EXTRA_FAMILY_NAME, familyName);
|
|
357
|
+
|
|
358
|
+
setResult(RESULT_OK, resultIntent);
|
|
359
|
+
finish();
|
|
360
|
+
} catch (Exception e) {
|
|
361
|
+
setResult(RESULT_CANCELED);
|
|
362
|
+
finish();
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
package/android/src/main/java/io/capawesome/capacitorjs/plugins/applesignin/AppleSignInPlugin.java
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.applesignin;
|
|
2
|
+
|
|
3
|
+
import android.content.Intent;
|
|
4
|
+
import androidx.activity.result.ActivityResult;
|
|
5
|
+
import androidx.annotation.NonNull;
|
|
6
|
+
import androidx.annotation.Nullable;
|
|
7
|
+
import com.getcapacitor.Logger;
|
|
8
|
+
import com.getcapacitor.Plugin;
|
|
9
|
+
import com.getcapacitor.PluginCall;
|
|
10
|
+
import com.getcapacitor.PluginMethod;
|
|
11
|
+
import com.getcapacitor.annotation.ActivityCallback;
|
|
12
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
13
|
+
import io.capawesome.capacitorjs.plugins.applesignin.classes.CustomException;
|
|
14
|
+
import io.capawesome.capacitorjs.plugins.applesignin.classes.options.SignInOptions;
|
|
15
|
+
import io.capawesome.capacitorjs.plugins.applesignin.classes.results.SignInResult;
|
|
16
|
+
import io.capawesome.capacitorjs.plugins.applesignin.interfaces.NonEmptyResultCallback;
|
|
17
|
+
import io.capawesome.capacitorjs.plugins.applesignin.interfaces.Result;
|
|
18
|
+
|
|
19
|
+
@CapacitorPlugin(name = "AppleSignIn")
|
|
20
|
+
public class AppleSignInPlugin extends Plugin {
|
|
21
|
+
|
|
22
|
+
public static final String TAG = "AppleSignIn";
|
|
23
|
+
public static final String ERROR_UNKNOWN_ERROR = "An unknown error has occurred.";
|
|
24
|
+
|
|
25
|
+
private AppleSignIn implementation;
|
|
26
|
+
|
|
27
|
+
@Override
|
|
28
|
+
public void load() {
|
|
29
|
+
this.implementation = new AppleSignIn();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@PluginMethod
|
|
33
|
+
public void initialize(PluginCall call) {
|
|
34
|
+
try {
|
|
35
|
+
String clientId = call.getString("clientId");
|
|
36
|
+
if (clientId == null) {
|
|
37
|
+
call.reject("clientId must be provided.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
implementation.initialize(clientId);
|
|
41
|
+
call.resolve();
|
|
42
|
+
} catch (Exception exception) {
|
|
43
|
+
rejectCall(call, exception);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@PluginMethod
|
|
48
|
+
public void signIn(PluginCall call) {
|
|
49
|
+
try {
|
|
50
|
+
SignInOptions options = new SignInOptions(call);
|
|
51
|
+
String url = implementation.buildAuthUrl(options);
|
|
52
|
+
|
|
53
|
+
Intent intent = new Intent(getContext(), AppleSignInActivity.class);
|
|
54
|
+
intent.putExtra(AppleSignInActivity.EXTRA_URL, url);
|
|
55
|
+
intent.putExtra(AppleSignInActivity.EXTRA_REDIRECT_URL, options.getRedirectUrl());
|
|
56
|
+
|
|
57
|
+
startActivityForResult(call, intent, "handleSignInResult");
|
|
58
|
+
} catch (Exception exception) {
|
|
59
|
+
rejectCall(call, exception);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@ActivityCallback
|
|
64
|
+
private void handleSignInResult(PluginCall call, ActivityResult activityResult) {
|
|
65
|
+
if (call == null) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
NonEmptyResultCallback<SignInResult> callback = new NonEmptyResultCallback<>() {
|
|
69
|
+
@Override
|
|
70
|
+
public void success(@NonNull SignInResult result) {
|
|
71
|
+
resolveCall(call, result);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Override
|
|
75
|
+
public void error(Exception exception) {
|
|
76
|
+
rejectCall(call, exception);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
implementation.handleActivityResult(activityResult.getResultCode(), activityResult.getData(), callback);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private void rejectCall(@NonNull PluginCall call, @NonNull Exception exception) {
|
|
83
|
+
String message = exception.getMessage();
|
|
84
|
+
if (message == null) {
|
|
85
|
+
message = ERROR_UNKNOWN_ERROR;
|
|
86
|
+
}
|
|
87
|
+
String code = null;
|
|
88
|
+
if (exception instanceof CustomException) {
|
|
89
|
+
code = ((CustomException) exception).getCode();
|
|
90
|
+
}
|
|
91
|
+
Logger.error(TAG, message, exception);
|
|
92
|
+
call.reject(message, code);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private void resolveCall(@NonNull PluginCall call) {
|
|
96
|
+
call.resolve();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private void resolveCall(@NonNull PluginCall call, @Nullable Result result) {
|
|
100
|
+
if (result == null) {
|
|
101
|
+
call.resolve();
|
|
102
|
+
} else {
|
|
103
|
+
call.resolve(result.toJSObject());
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.applesignin.classes;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
|
|
6
|
+
public class CustomException extends Exception {
|
|
7
|
+
|
|
8
|
+
@Nullable
|
|
9
|
+
private final String code;
|
|
10
|
+
|
|
11
|
+
public CustomException(@Nullable String code, @NonNull String message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Nullable
|
|
17
|
+
public String getCode() {
|
|
18
|
+
return code;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.applesignin.classes;
|
|
2
|
+
|
|
3
|
+
public class CustomExceptions {
|
|
4
|
+
|
|
5
|
+
public static final CustomException CLIENT_ID_MISSING = new CustomException(
|
|
6
|
+
null,
|
|
7
|
+
"clientId must be provided. Call initialize() first."
|
|
8
|
+
);
|
|
9
|
+
public static final CustomException REDIRECT_URL_MISSING = new CustomException(null, "redirectUrl must be provided.");
|
|
10
|
+
public static final CustomException SIGN_IN_CANCELED = new CustomException("SIGN_IN_CANCELED", "Sign in was canceled.");
|
|
11
|
+
public static final CustomException SIGN_IN_FAILED = new CustomException(null, "Sign in failed.");
|
|
12
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.applesignin.classes.options;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.getcapacitor.JSArray;
|
|
6
|
+
import com.getcapacitor.PluginCall;
|
|
7
|
+
import io.capawesome.capacitorjs.plugins.applesignin.classes.CustomExceptions;
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.List;
|
|
10
|
+
import org.json.JSONArray;
|
|
11
|
+
|
|
12
|
+
public class SignInOptions {
|
|
13
|
+
|
|
14
|
+
@NonNull
|
|
15
|
+
private final String redirectUrl;
|
|
16
|
+
|
|
17
|
+
@NonNull
|
|
18
|
+
private final List<String> scopes;
|
|
19
|
+
|
|
20
|
+
@Nullable
|
|
21
|
+
private final String nonce;
|
|
22
|
+
|
|
23
|
+
@Nullable
|
|
24
|
+
private final String state;
|
|
25
|
+
|
|
26
|
+
public SignInOptions(@NonNull PluginCall call) throws Exception {
|
|
27
|
+
this.redirectUrl = getRedirectUrlFromCall(call);
|
|
28
|
+
this.scopes = getScopesFromCall(call);
|
|
29
|
+
this.nonce = call.getString("nonce");
|
|
30
|
+
this.state = call.getString("state");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@NonNull
|
|
34
|
+
public String getRedirectUrl() {
|
|
35
|
+
return redirectUrl;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@NonNull
|
|
39
|
+
public List<String> getScopes() {
|
|
40
|
+
return scopes;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Nullable
|
|
44
|
+
public String getNonce() {
|
|
45
|
+
return nonce;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Nullable
|
|
49
|
+
public String getState() {
|
|
50
|
+
return state;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@NonNull
|
|
54
|
+
private static String getRedirectUrlFromCall(@NonNull PluginCall call) throws Exception {
|
|
55
|
+
String redirectUrl = call.getString("redirectUrl");
|
|
56
|
+
if (redirectUrl == null) {
|
|
57
|
+
throw CustomExceptions.REDIRECT_URL_MISSING;
|
|
58
|
+
}
|
|
59
|
+
return redirectUrl;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@NonNull
|
|
63
|
+
private static List<String> getScopesFromCall(@NonNull PluginCall call) {
|
|
64
|
+
List<String> scopes = new ArrayList<>();
|
|
65
|
+
JSArray scopesArray = call.getArray("scopes");
|
|
66
|
+
if (scopesArray == null) {
|
|
67
|
+
return scopes;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
JSONArray jsonArray = scopesArray;
|
|
71
|
+
for (int i = 0; i < jsonArray.length(); i++) {
|
|
72
|
+
String scope = jsonArray.getString(i);
|
|
73
|
+
if ("EMAIL".equals(scope)) {
|
|
74
|
+
scopes.add("email");
|
|
75
|
+
} else if ("FULL_NAME".equals(scope)) {
|
|
76
|
+
scopes.add("name");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch (Exception e) {
|
|
80
|
+
// ignore
|
|
81
|
+
}
|
|
82
|
+
return scopes;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
package io.capawesome.capacitorjs.plugins.applesignin.classes.results;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.NonNull;
|
|
4
|
+
import androidx.annotation.Nullable;
|
|
5
|
+
import com.getcapacitor.JSObject;
|
|
6
|
+
import io.capawesome.capacitorjs.plugins.applesignin.interfaces.Result;
|
|
7
|
+
import org.json.JSONObject;
|
|
8
|
+
|
|
9
|
+
public class SignInResult implements Result {
|
|
10
|
+
|
|
11
|
+
@NonNull
|
|
12
|
+
private final String authorizationCode;
|
|
13
|
+
|
|
14
|
+
@NonNull
|
|
15
|
+
private final String idToken;
|
|
16
|
+
|
|
17
|
+
@NonNull
|
|
18
|
+
private final String user;
|
|
19
|
+
|
|
20
|
+
@Nullable
|
|
21
|
+
private final String email;
|
|
22
|
+
|
|
23
|
+
@Nullable
|
|
24
|
+
private final String givenName;
|
|
25
|
+
|
|
26
|
+
@Nullable
|
|
27
|
+
private final String familyName;
|
|
28
|
+
|
|
29
|
+
@Nullable
|
|
30
|
+
private final String state;
|
|
31
|
+
|
|
32
|
+
public SignInResult(
|
|
33
|
+
@NonNull String authorizationCode,
|
|
34
|
+
@NonNull String idToken,
|
|
35
|
+
@NonNull String user,
|
|
36
|
+
@Nullable String email,
|
|
37
|
+
@Nullable String givenName,
|
|
38
|
+
@Nullable String familyName,
|
|
39
|
+
@Nullable String state
|
|
40
|
+
) {
|
|
41
|
+
this.authorizationCode = authorizationCode;
|
|
42
|
+
this.idToken = idToken;
|
|
43
|
+
this.user = user;
|
|
44
|
+
this.email = email;
|
|
45
|
+
this.givenName = givenName;
|
|
46
|
+
this.familyName = familyName;
|
|
47
|
+
this.state = state;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Override
|
|
51
|
+
@NonNull
|
|
52
|
+
public JSObject toJSObject() {
|
|
53
|
+
JSObject result = new JSObject();
|
|
54
|
+
result.put("authorizationCode", authorizationCode);
|
|
55
|
+
result.put("idToken", idToken);
|
|
56
|
+
result.put("user", user);
|
|
57
|
+
result.put("email", email == null ? JSONObject.NULL : email);
|
|
58
|
+
result.put("givenName", givenName == null ? JSONObject.NULL : givenName);
|
|
59
|
+
result.put("familyName", familyName == null ? JSONObject.NULL : familyName);
|
|
60
|
+
if (state != null) {
|
|
61
|
+
result.put("state", state);
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
File without changes
|