capacitor-sso-webview 0.0.7 → 0.0.8

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.
@@ -4,6 +4,9 @@ import android.app.Activity;
4
4
  import android.app.Dialog;
5
5
  import android.graphics.Color;
6
6
  import android.net.Uri;
7
+ import android.os.Handler;
8
+ import android.os.Looper;
9
+ import android.util.Log;
7
10
  import android.view.Gravity;
8
11
  import android.view.ViewGroup;
9
12
  import android.webkit.CookieManager;
@@ -14,8 +17,23 @@ import android.webkit.WebViewClient;
14
17
  import android.widget.LinearLayout;
15
18
  import android.widget.TextView;
16
19
 
20
+ import java.io.BufferedReader;
21
+ import java.io.IOException;
22
+ import java.io.InputStream;
23
+ import java.io.InputStreamReader;
24
+ import java.net.HttpURLConnection;
25
+ import java.net.URL;
26
+ import java.nio.charset.StandardCharsets;
27
+ import java.util.concurrent.ExecutorService;
28
+ import java.util.concurrent.Executors;
29
+
17
30
  public class SSOWebView {
18
31
 
32
+ private static final String TAG = "SSOWebView";
33
+
34
+ /* Single background thread — cukup untuk satu exchange per login attempt */
35
+ private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
36
+
19
37
  public interface Listener {
20
38
  void onRedirectIntercepted(String url, String code, String state, String body);
21
39
 
@@ -25,7 +43,6 @@ public class SSOWebView {
25
43
  private Dialog dialog;
26
44
  private WebView webView;
27
45
  private boolean alreadyRedirected = false;
28
- private boolean bodyRead = false;
29
46
 
30
47
  public void open(
31
48
  final Activity activity,
@@ -35,7 +52,6 @@ public class SSOWebView {
35
52
  final String title,
36
53
  final Listener listener) {
37
54
  alreadyRedirected = false;
38
- bodyRead = false;
39
55
 
40
56
  dialog = new Dialog(activity, android.R.style.Theme_Light_NoTitleBar_Fullscreen);
41
57
 
@@ -105,81 +121,30 @@ public class SSOWebView {
105
121
 
106
122
  if (isRedirect && !alreadyRedirected) {
107
123
  alreadyRedirected = true;
108
- /* Biarkan WebView navigate — server akan return JSON body */
109
- return false;
110
- }
111
- return false;
112
- }
113
-
114
- @Override
115
- public void onPageFinished(WebView view, String pageUrl) {
116
- super.onPageFinished(view, pageUrl);
117
-
118
- /* Guard: hanya proses sekali setelah redirect */
119
- if (bodyRead) return;
120
-
121
- Uri u = Uri.parse(pageUrl);
122
- String host = u.getHost();
123
- String path = u.getPath();
124
-
125
- boolean isRedirectPage =
126
- host != null && host.equalsIgnoreCase(redirectHost) &&
127
- path != null && path.toLowerCase()
128
- .contains(redirectPathContains.toLowerCase());
129
124
 
130
- if (!isRedirectPage) return;
131
-
132
- /* Set flag SEGERA — cegah double-fire */
133
- bodyRead = true;
134
-
135
- final String finalUrl = pageUrl;
125
+ final String finalUrl = u.toString();
136
126
  final String finalCode = u.getQueryParameter("code") != null
137
127
  ? u.getQueryParameter("code") : "";
138
128
  final String finalState = u.getQueryParameter("state") != null
139
129
  ? u.getQueryParameter("state") : "";
140
130
 
141
- /* Delay 150ms — beri waktu body JSON ter-render ke DOM */
142
- view.postDelayed(new Runnable() {
143
- @Override
144
- public void run() {
145
- if (webView == null) return;
146
- webView.evaluateJavascript(
147
- "(function(){" +
148
- " var t = document.body" +
149
- " ? (document.body.innerText || document.body.textContent || '')" +
150
- " : '';" +
151
- " return t.trim();" +
152
- "})()",
153
- value -> {
154
- String body = value;
155
- if (body != null) {
156
- if (body.startsWith("\"") && body.endsWith("\""))
157
- body = body.substring(1, body.length() - 1);
158
- body = body.replace("\\\"", "\"")
159
- .replace("\\n", "\n")
160
- .replace("\\r", "")
161
- .replace("\\/", "/");
162
- }
163
-
164
- final String finalBody = (body != null) ? body : "";
165
- android.util.Log.d("SSOWebView",
166
- "body len=" + finalBody.length() +
167
- " preview=" + finalBody.substring(
168
- 0, Math.min(120, finalBody.length())));
169
-
170
- activity.runOnUiThread(() -> SSOWebView.this.dismiss());
171
-
172
- if (listener != null)
173
- listener.onRedirectIntercepted(
174
- finalUrl, finalCode, finalState, finalBody);
175
- }
176
- );
177
- }
178
- }, 150);
179
- }
180
-
181
- private void dismiss() {
182
- SSOWebView.this.dismiss();
131
+ /*
132
+ * PENTING: BATALKAN navigasi (return true), jangan biarkan WebView
133
+ * merender URL ini. Endpoint redirect mengembalikan JSON, bukan
134
+ * HTML — jika WebView mencoba menampilkannya sebagai halaman, hasil
135
+ * yang didapat adalah halaman error native "Webpage not available",
136
+ * bukan body JSON, sehingga JSON.parse di sisi JS selalu gagal.
137
+ *
138
+ * Sebagai gantinya, ambil body via HTTP request native di background
139
+ * thread — pola yang sama seperti fetchJsonFromUrl() pada
140
+ * LoginIdamActivity, yang sudah terbukti bekerja untuk flow login
141
+ * berbasis WebView redirect lainnya di aplikasi ini.
142
+ */
143
+ dismiss();
144
+ fetchRedirectBody(finalUrl, finalCode, finalState, listener);
145
+ return true;
146
+ }
147
+ return false;
183
148
  }
184
149
  });
185
150
 
@@ -199,6 +164,64 @@ public class SSOWebView {
199
164
  webView.loadUrl(url);
200
165
  }
201
166
 
167
+ private void fetchRedirectBody(
168
+ final String url,
169
+ final String code,
170
+ final String state,
171
+ final Listener listener) {
172
+
173
+ EXECUTOR.execute(() -> {
174
+ String body;
175
+ HttpURLConnection conn = null;
176
+ try {
177
+ conn = (HttpURLConnection) new URL(url).openConnection();
178
+ conn.setRequestMethod("GET");
179
+ conn.setConnectTimeout(20000);
180
+ conn.setReadTimeout(20000);
181
+ conn.setRequestProperty("Accept", "application/json");
182
+
183
+ int status = conn.getResponseCode();
184
+ InputStream is = (status >= 200 && status < 400)
185
+ ? conn.getInputStream() : conn.getErrorStream();
186
+ body = readStream(is);
187
+
188
+ Log.d(TAG, "fetchRedirectBody status=" + status + " len=" + body.length());
189
+ } catch (Exception e) {
190
+ Log.e(TAG, "fetchRedirectBody failed", e);
191
+ /*
192
+ * Bungkus error sebagai JSON agar konsumen JS (yang selalu mem-parse
193
+ * body sebagai JSON dan memeriksa `status`) mendapat pesan yang jelas,
194
+ * alih-alih body kosong atau string mentah yang gagal di-parse.
195
+ */
196
+ body = "{\"status\":0,\"status_message\":\"" + escapeJson(e.getMessage()) + "\"}";
197
+ } finally {
198
+ if (conn != null) conn.disconnect();
199
+ }
200
+
201
+ final String finalBody = body;
202
+ new Handler(Looper.getMainLooper()).post(() -> {
203
+ if (listener != null) {
204
+ listener.onRedirectIntercepted(url, code, state, finalBody);
205
+ }
206
+ });
207
+ });
208
+ }
209
+
210
+ private static String readStream(InputStream is) throws IOException {
211
+ if (is == null) return "";
212
+ StringBuilder sb = new StringBuilder();
213
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
214
+ String line;
215
+ while ((line = br.readLine()) != null) sb.append(line);
216
+ }
217
+ return sb.toString();
218
+ }
219
+
220
+ private static String escapeJson(String s) {
221
+ if (s == null) return "Unknown network error";
222
+ return s.replace("\\", "\\\\").replace("\"", "\\\"");
223
+ }
224
+
202
225
  public void dismiss() {
203
226
  try {
204
227
  if (webView != null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-sso-webview",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "SSO WebView with URL intercept",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",