-
Notifications
You must be signed in to change notification settings - Fork 3
feat: AJAX uses token authentication #181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
d1ba186
a6ff652
f4ecea8
7013fe8
9c10108
88974ad
e9082c9
f912eef
3c57aa2
bbaf9c4
394bce2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,14 +30,14 @@ import org.json.JSONException | |
| import org.json.JSONObject | ||
| import java.util.Locale | ||
|
|
||
| const val ASSET_URL = "https://appassets.androidplatform.net/assets/index.html" | ||
| const val DEFAULT_ASSET_DOMAIN = "appassets.androidplatform.net" | ||
| const val ASSET_PATH_INDEX = "/assets/index.html" | ||
|
|
||
| class GutenbergView : WebView { | ||
| private var isEditorLoaded = false | ||
| private var didFireEditorLoaded = false | ||
| private var assetLoader = WebViewAssetLoader.Builder() | ||
| .addPathHandler("/assets/", AssetsPathHandler(this.context)) | ||
| .build() | ||
| private lateinit var assetLoader: WebViewAssetLoader | ||
| private lateinit var assetDomain: String | ||
| private var configuration: EditorConfiguration = EditorConfiguration.builder().build() | ||
|
|
||
| private val handler = Handler(Looper.getMainLooper()) | ||
|
|
@@ -149,7 +149,7 @@ class GutenbergView : WebView { | |
| ): WebResourceResponse? { | ||
| if (request.url == null) { | ||
| return super.shouldInterceptRequest(view, request) | ||
| } else if (request.url.host?.contains("appassets.androidplatform.net") == true) { | ||
| } else if (request.url.host == assetDomain) { | ||
| return assetLoader.shouldInterceptRequest(request.url) | ||
| } else if (requestInterceptor.canIntercept(request)) { | ||
| return requestInterceptor.handleRequest(request) | ||
|
|
@@ -182,7 +182,7 @@ class GutenbergView : WebView { | |
| } | ||
|
|
||
| // Allow asset URLs | ||
| if (url.host == Uri.parse(ASSET_URL).host) { | ||
| if (url.host == assetDomain) { | ||
| return false | ||
| } | ||
|
|
||
|
|
@@ -258,6 +258,15 @@ class GutenbergView : WebView { | |
| fun start(configuration: EditorConfiguration) { | ||
| this.configuration = configuration | ||
|
|
||
| // Set up asset loader domain | ||
| assetDomain = configuration.assetLoaderDomain ?: DEFAULT_ASSET_DOMAIN | ||
|
|
||
| // Initialize asset loader with configured domain | ||
| assetLoader = WebViewAssetLoader.Builder() | ||
| .setDomain(assetDomain) | ||
| .addPathHandler("/assets/", AssetsPathHandler(this.context)) | ||
|
Comment on lines
260
to
+267
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great implementation! The configurable
This enables Android apps to configure a domain that their WordPress site allows in CORS policies, which is essential for AJAX functionality to work. |
||
| .build() | ||
|
|
||
| // Set up asset caching if enabled | ||
| if (configuration.enableAssetCaching) { | ||
| val library = EditorAssetsLibrary(context, configuration) | ||
|
|
@@ -273,13 +282,13 @@ class GutenbergView : WebView { | |
| val editorUrl = if (BuildConfig.GUTENBERG_EDITOR_URL.isNotEmpty()) { | ||
| BuildConfig.GUTENBERG_EDITOR_URL | ||
| } else { | ||
| ASSET_URL | ||
| "https://$assetDomain$ASSET_PATH_INDEX" | ||
| } | ||
|
|
||
| WebStorage.getInstance().deleteAllData() | ||
| this.clearCache(true) | ||
| // All cookies are third-party cookies because the root of this document | ||
| // lives under `https://appassets.androidplatform.net` | ||
| // lives under the configured asset domain (e.g., `https://appassets.androidplatform.net`) | ||
| CookieManager.getInstance().setAcceptThirdPartyCookies(this, true); | ||
|
|
||
| // Erase all local cookies before loading the URL – we don't want to persist | ||
|
|
@@ -302,6 +311,7 @@ class GutenbergView : WebView { | |
|
|
||
| val gbKitConfig = """ | ||
| window.GBKit = { | ||
| "siteURL": "${configuration.siteURL}", | ||
| "siteApiRoot": "${configuration.siteApiRoot}", | ||
| "siteApiNamespace": ${configuration.siteApiNamespace.joinToString(",", "[", "]") { "\"$it\"" }}, | ||
| "namespaceExcludedPaths": ${configuration.namespaceExcludedPaths.joinToString(",", "[", "]") { "\"$it\"" }}, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /** | ||
| * Internal dependencies | ||
| */ | ||
| import { getGBKit } from './bridge'; | ||
| import { warn, debug } from './logger'; | ||
|
|
||
| /** | ||
| * GutenbergKit lacks authentication cookies required for AJAX requests. | ||
| * This configures a root URL and authentication header for AJAX requests. | ||
| * | ||
| * @return {void} | ||
| */ | ||
| export function configureAjax() { | ||
| window.wp = window.wp || {}; | ||
| window.wp.ajax = window.wp.ajax || {}; | ||
| window.wp.ajax.settings = window.wp.ajax.settings || {}; | ||
|
|
||
| const { siteURL, authHeader } = getGBKit(); | ||
| configureAjaxUrl( siteURL ); | ||
| configureAjaxAuth( authHeader ); | ||
| } | ||
|
|
||
| function configureAjaxUrl( siteURL ) { | ||
| if ( ! siteURL ) { | ||
| warn( 'Unable to configure AJAX URL without siteURL' ); | ||
| return; | ||
| } | ||
|
|
||
| // Global used within WordPress admin pages | ||
| window.ajaxurl = `${ siteURL }/wp-admin/admin-ajax.php`; | ||
| // Global used by WordPress' JavaScript API | ||
| window.wp.ajax.settings.url = `${ siteURL }/wp-admin/admin-ajax.php`; | ||
|
|
||
| debug( 'AJAX URL configured' ); | ||
| } | ||
|
|
||
| function configureAjaxAuth( authHeader ) { | ||
| if ( ! authHeader ) { | ||
| warn( 'Unable to configure AJAX auth without authHeader' ); | ||
| return; | ||
| } | ||
|
|
||
| window.jQuery?.ajaxSetup( { | ||
| headers: { | ||
| Authorization: authHeader, | ||
| }, | ||
| } ); | ||
|
Comment on lines
+43
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor observation: This sets the authorization header globally for all jQuery AJAX requests. Combined with the per-request header setting in the This is harmless and provides defense-in-depth, but worth noting. The redundancy ensures the auth header is present even if one mechanism fails. |
||
|
|
||
| if ( typeof window.wp.ajax.send === 'function' ) { | ||
| const originalSend = window.wp.ajax.send; | ||
| window.wp.ajax.send = function ( options ) { | ||
| const originalBeforeSend = options.beforeSend; | ||
|
|
||
| options.beforeSend = function ( xhr ) { | ||
| xhr.setRequestHeader( 'Authorization', authHeader ); | ||
|
|
||
| if ( typeof originalBeforeSend === 'function' ) { | ||
| originalBeforeSend( xhr ); | ||
| } | ||
| }; | ||
|
|
||
| return originalSend.call( this, options ); | ||
| }; | ||
| } | ||
|
|
||
| if ( typeof window.wp.ajax.post === 'function' ) { | ||
| const originalPost = window.wp.ajax.post; | ||
| window.wp.ajax.post = function ( options ) { | ||
| const originalBeforeSend = options.beforeSend; | ||
|
|
||
| options.beforeSend = function ( xhr ) { | ||
| xhr.setRequestHeader( 'Authorization', authHeader ); | ||
|
|
||
| if ( typeof originalBeforeSend === 'function' ) { | ||
| originalBeforeSend( xhr ); | ||
| } | ||
| }; | ||
|
|
||
| return originalPost.call( this, options ); | ||
| }; | ||
| } | ||
|
|
||
| debug( 'AJAX auth configured' ); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great addition! The
assetLoaderDomainconfiguration properly addresses AJAX CORS requirements for Android. The nullable type withnulldefault is correct since it falls back toDEFAULT_ASSET_DOMAINif not specified (GutenbergView.kt:262).The documentation in
docs/integration.mdclearly explains that this must be set to a domain allowed by the WordPress site's CORS policy for AJAX to work on Android.