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
|
-
|
|
107
|
-
|
|
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) {
|