capacitor-sso-webview 0.0.6 → 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
 
@@ -103,78 +121,31 @@ public class SSOWebView {
103
121
 
104
122
  if (isRedirect && !alreadyRedirected) {
105
123
  alreadyRedirected = true;
106
- /* Biarkan WebView navigate — server akan return JSON body */
107
- return false;
124
+
125
+ final String finalUrl = u.toString();
126
+ final String finalCode = u.getQueryParameter("code") != null
127
+ ? u.getQueryParameter("code") : "";
128
+ final String finalState = u.getQueryParameter("state") != null
129
+ ? u.getQueryParameter("state") : "";
130
+
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;
108
146
  }
109
147
  return false;
110
148
  }
111
-
112
- @Override
113
- public void onPageFinished(WebView view, String pageUrl) {
114
- super.onPageFinished(view, pageUrl);
115
-
116
- Uri u = Uri.parse(pageUrl);
117
- String host = u.getHost();
118
- String path = u.getPath();
119
-
120
- boolean isRedirectPage = host != null && host.equalsIgnoreCase(redirectHost) &&
121
- path != null && path.toLowerCase()
122
- .contains(redirectPathContains.toLowerCase());
123
-
124
- if (!isRedirectPage)
125
- return;
126
-
127
- final String finalUrl = pageUrl;
128
- final String finalCode = u.getQueryParameter("code") != null ?
129
- u.getQueryParameter("code") :
130
- "";
131
- final String finalState = u.getQueryParameter("state") != null ?
132
- u.getQueryParameter("state") :
133
- "";
134
-
135
- /* Delay 150ms — beri waktu body JSON ter-render ke DOM */
136
- view.postDelayed(new Runnable() {
137
- @Override
138
- public void run() {
139
- if (webView == null) return;
140
- webView.evaluateJavascript(
141
- "(function(){" +
142
- " var t = document.body" +
143
- " ? (document.body.innerText || document.body.textContent || '')" +
144
- " : '';" +
145
- " return t.trim();" +
146
- "})()",
147
- value -> {
148
- String body = value;
149
- if (body != null) {
150
- if (body.startsWith("\"") && body.endsWith("\""))
151
- body = body.substring(1, body.length() - 1);
152
- body = body.replace("\\\"", "\"")
153
- .replace("\\n", "\n")
154
- .replace("\\r", "")
155
- .replace("\\/", "/");
156
- }
157
- final String finalBody = (body != null) ? body : "";
158
- android.util.Log.d("SSOWebView",
159
- "body len=" + finalBody.length() +
160
- " preview=" + finalBody.substring(
161
- 0, Math.min(120, finalBody.length())));
162
-
163
- /* FIX: gunakan outer class reference */
164
- activity.runOnUiThread(() -> SSOWebView.this.dismiss());
165
-
166
- if (listener != null)
167
- listener.onRedirectIntercepted(
168
- finalUrl, finalCode, finalState, finalBody);
169
- }
170
- );
171
- }
172
- }, 150);
173
- }
174
-
175
- private void dismiss() {
176
- SSOWebView.this.dismiss();
177
- }
178
149
  });
179
150
 
180
151
  webView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
@@ -193,6 +164,64 @@ public class SSOWebView {
193
164
  webView.loadUrl(url);
194
165
  }
195
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
+
196
225
  public void dismiss() {
197
226
  try {
198
227
  if (webView != null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-sso-webview",
3
- "version": "0.0.6",
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",