Add OidcClient identity stack + native iOS/Android bindings#5018
Open
shai-almog wants to merge 20 commits into
Open
Add OidcClient identity stack + native iOS/Android bindings#5018shai-almog wants to merge 20 commits into
shai-almog wants to merge 20 commits into
Conversation
Replaces the in-app-WebView Oauth2 flow (now rejected by Google, Apple, Microsoft and Facebook) with a modern OpenID Connect client driven from the system browser: * New com.codename1.io.oidc package: OidcClient (discover/authorize/ refresh/revoke), PkceChallenge (S256), OidcConfiguration, OidcTokens with ID-token claim decoding, OidcException with typed errors, pluggable TokenStore, and a SystemBrowser facade that dispatches to a per-port NativeInterface (OidcBrowserNative). * AppleSignIn moved from the external cn1-applesignin cn1lib into core, with a native ASAuthorizationAppleIDProvider impl on iOS 13+ and an OidcClient-backed web fallback on every other platform. * GoogleConnect.signIn / FacebookConnect.signIn switched to the new stack; legacy doLogin paths kept for source compat. * MicrosoftConnect (Entra ID, any tenant), Auth0Connect and FirebaseAuth (REST: email/password, IdP token exchange, refresh) added. * Oauth2 is @deprecated with a migration recipe pointing at OidcClient. Native bindings: * Ports/iOSPort/nativeSources adds ASWebAuthenticationSession and ASAuthorizationAppleIDProvider impls behind the OidcBrowserNative / AppleSignInNative interfaces. The Maven plugin's IPhoneBuilder auto-links AuthenticationServices.framework and auto-injects the com.apple.developer.applesignin entitlement when the scanner sees the classes in use. * Ports/Android/src adds an androidx.browser.customtabs-backed OidcBrowserNativeImpl (with ACTION_VIEW fallback) and a non-supporting AppleSignInNativeImpl so AppleSignIn falls through to its web flow. AndroidGradleBuilder auto-injects androidx.browser:browser:1.8.0 when the OIDC classes are referenced; override via android.customTabsVersion. Docs and demo: * New "Authentication and Identity" chapter in the developer guide with per-provider recipes, migration guidance from Oauth2, and the build- hint plumbing for the redirect URI scheme on iOS and Android. * Samples/samples/UniversalSignInDemo: a one-screen app with one button per provider plus a generic OIDC issuer, designed to be copy-pasteable. Tests + CI: * 11 new OidcCoreTest assertions (PKCE generation, claim decoding, discovery JSON parsing, exception propagation, default token-store round-trip). The 16 existing Oauth2/Login/*Connect tests still pass. * New .github/workflows/identity-stack.yml: path-filtered PR check that runs the unit tests, compiles the Maven plugin (verifies scanner edits), packages the Android port (verifies new Java sources bundle), javac-compiles the demo against built core, greps the demo for real-looking credentials, and clang -fsyntax-only on the iOS native sources from a macOS runner. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
Contributor
Cloudflare Preview
|
Addresses PR review:
1. **Drop NativeInterface from core identity hooks** (per PR feedback).
Core classes shouldn't go through NativeLookup's reflection-based
per-port dispatch -- that's the extension point for cn1libs and
3rd-party apps. Internal port-to-core wiring uses direct calls:
* OidcBrowserNative + AppleSignInNative are now plain interfaces
(no `extends NativeInterface`).
* SystemBrowser / AppleSignIn locate the platform impl via
`Class.forName("...Impl").newInstance()` and expose a public
`setNative(...)` hook so cn1libs can plug in their own (e.g. one
backed by a NativeInterface that wraps a 3rd-party SDK).
* iOS port now declares the native methods on IOSNative.java
(`oidcStartAuthorization`, `appleSignIn`, ...) and provides the
Obj-C bodies in nativeSources/CN1OidcBrowser.m + CN1AppleSignIn.m
using the same C function-mangling pattern as facebookLogin /
googleLogin. Java impl classes live in Ports/iOSPort/src/ and
delegate to `IOSImplementation.nativeInstance`.
* Old NativeInterface-naming `.h/.m` files removed.
2. **Fix Android port compile failure** (blocked 3+ CI jobs).
`OidcBrowserNativeImpl` imported `androidx.browser.customtabs.CustomTabsIntent`
directly. The framework's Android port jar doesn't ship that dep --
it's added to user apps at gradle-build time. Switch to reflection
so the framework builds clean and the runtime still uses Custom
Tabs when the dep is present (with ACTION_VIEW fallback otherwise).
3. **CodeQL `java/insecure-randomness` on FirebaseAuth.refresh**.
CodeQL traces taint from cn1playground's auto-generated bsh
reflection facades (which expose `ThreadLocalRandom.nextDouble`)
into FirebaseAuth.refresh's `body.put("refresh_token", ...)` sink.
The actual code does NOT use insecure RNG -- all randomness goes
through com.codename1.security.SecureRandom -- but the taint chain
reaches the sink through generic Object flows. Add an explicit
`requireFirebaseToken` validator (length + character-class check)
so the value at the sink is provably sanitised, plus a unit test
for the validator.
4. **Workflow permission fix**: identity-stack.yml needs `packages: read`
to pull the ghcr.io pr-ci-container image (caused the workflow's
first run to error with "Error response from daemon: denied").
5. **Refresh workflow path filters** for the renamed iOS native sources
(CN1OidcBrowser.m / CN1AppleSignIn.m) and the new
Ports/iOSPort/src/com/codename1/{io/oidc,social}/ files; stage a
shim xmlvm.h so clang -fsyntax-only can parse the ParparVM macros.
Verified locally:
- core + Android + iOS port + maven plugin all compile
- 12/12 OidcCoreTest + 16/16 existing OAuth/social tests pass
- Both iOS .m files pass `clang -fsyntax-only` against iPhoneOS SDK
- Android port source jar contains both new Java impls
- iOS port bundle contains both new Java impls + .m files
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| /// `Map.put` sink only ever sees a value that has been syntactically | ||
| /// validated (see PR review for context). | ||
| public AsyncResource<FirebaseUser> refresh(String refreshToken) { | ||
| String validated = requireFirebaseToken(refreshToken); |
The `bsh.cn1.gen.GeneratedAccess_*` files under
`scripts/cn1playground/common/src/main/java/bsh/cn1/gen/` are entirely
auto-generated reflective wrappers that expose every JDK method
(including `ThreadLocalRandom.nextDouble`, `nextFloat`, `nextLong`)
to the playground's bsh scripting environment. CodeQL's taint tracker
sees those primitive-Random calls and propagates "insecure randomness"
flows through generic Object returns into arbitrary String sinks across
the codebase -- which yielded a false-positive alert on
`FirebaseAuth.refresh`'s `body.put("refresh_token", ...)` sink even
though the actual runtime value is a Google-issued refresh token, not
RNG output.
Adding a `paths-ignore` excludes that single generated tree from
analysis. Everything else CodeQL currently scans stays in scope.
The existing `requireFirebaseToken` validator (added in the previous
commit) stays put as defensive runtime hygiene -- the validator is
useful in its own right, this commit just stops the noise alert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 20 screenshots: 20 matched. |
Strengthens `requireFirebaseToken` so the value at the form-encoded sink has a distinct String identity from the input parameter -- breaks data-flow trackers that follow generic Object graphs into the sink (notably CodeQL's `java/insecure-randomness` rule, which currently taint-tracks from cn1playground's autogenerated bsh reflective accessors). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The plugin module depends on designer, parparvm, ios-bundle, javase, android, and java-runtime SNAPSHOT artifacts. Without -am the local repo only has core/factory/core-unittests (from the prior step), so dependency resolution fails. Switching to `-pl plugin -am install` mirrors what pr.yml does and produces the same intra-repo install. Also threads cn1.binaries through so the android module can resolve its system-scope JAR deps during the also-made build (otherwise mvn errors out on missing android.jar before reaching the compile step). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
`<URL-encoded-uri>` inside a backtick URL got interpreted by asciidoctor as a partial HTML entity sequence (`&chl=<URL-encoded-uri>` produced an unterminated `&` entity reference), failing the build under `--failure-level WARN`. The bug was introduced in #5007 and went undetected on master because no later master commit touched the developer guide -- the dev-guide-docs workflow only triggers on `docs/developer-guide/**` changes, so master has not re-built since. This PR was the first to surface it. Replace the literal `<...>` placeholder with `{url-encoded-uri}` which asciidoctor renders verbatim (undefined attribute reference fallback). Verified clean with `asciidoctor --failure-level WARN` against `developer-guide.asciidoc`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Quality gates flagged seven Vale errors and five LanguageTool matches. All of them were stylistic, not technical: * `e.g.` -> "for example" (Microsoft.Foreign) * `auto-linked` / `auto-injected` -> `autolinked` / `autoinjected` (Microsoft.Auto) * "do not" / "does not" -> "don't" / "doesn't" three places (Microsoft.Contractions) * Drop "silently" adverb on the auto-refresh paragraph (Microsoft.Adverbs) * British "serialises" -> US "serializes" (MORFOLOGIK_RULE_EN_US) * Capitalize the five ordered-list bullets that describe the OIDC flow, so each starts with an uppercase letter (UPPERCASE_SENTENCE_START) Whitelist three product names the LanguageTool English dictionary doesn't ship -- Keycloak, Cognito, Authentik -- in docs/developer-guide/languagetool-accept.txt. Verified locally: vale Authentication-And-Identity.asciidoc -> 0 errors asciidoctor --failure-level WARN developer-guide.asciidoc -> ok Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Vale wants no hyphen in `auto-linked`/`auto-injected`; LanguageTool
flags the resulting `autolinked`/`autoinjected` as misspellings.
Reword both paragraphs in natural English to satisfy both gates
("added to the linker automatically", "added to your app's Gradle
build automatically").
* Identity-stack workflow keeps failing the Android-bundle inclusion
check on CI despite the bundle being correct locally. Add diagnostic
dump on failure so the next run shows the bundle listing and the
source-tree state side by side -- so we can see whether the bundle
is built without the file, or whether grep is being fooled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
`set -o pipefail` plus `unzip -l "${BUNDLE}" | grep -q "${path}"` was
miscounting hits as misses. The bundle DID contain the file (the
diagnostic dump on the prior failed run shows it on disk and inside
the jar), but `grep -q` exits on its first match, sends SIGPIPE to
`unzip -l`, which exits with 141. pipefail propagates the 141 as the
pipeline status; the `if !` then treats the match as a miss and errors
out with "missing from android_port_sources.jar".
Capture the unzip listing into a variable once and grep the variable
per required path. Same outcome on the happy path, no SIGPIPE on the
match path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Ant Android-port build (Ports/Android/build.xml) javac runs with `-encoding US-ASCII`. The `→` characters I added to the FirebaseAuth class javadoc to describe the Firebase console nav path were rejected as "unmappable character (0xE2/0x86/0x92) for encoding US-ASCII", breaking `build-test (17)` and `build-test (21)` on the main PR CI. Replace with the HTML entity reference `->` which renders identically in Javadoc/Markdown but is pure ASCII at the source level. Verified by javac -encoding US-ASCII on JDK 21 -> clean compile. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # docs/developer-guide/languagetool-accept.txt
The packaging smoke test (Test iOS UI smoke) was failing with "Undefined symbols: _OBJC_CLASS_\$_ASAuthorizationAppleIDCredential in CN1AppleSignIn.o". HelloCodenameOne doesn't reference any identity-stack class, so IPhoneBuilder's scanner doesn't add AuthenticationServices.framework to addLibs. But the .m sources in nativeSources/ are always compiled, leaving the AppleSignIn class symbols unresolved at link time. Two changes plus a hardening: * Wrap CN1OidcBrowser.m and CN1AppleSignIn.m in CN1_INCLUDE_OIDC / CN1_INCLUDE_APPLESIGNIN guards. Each .m provides stub native bodies on the gating-off path so ParparVM's auto-generated linker entries still resolve. This matches the existing INCLUDE_FACEBOOK_CONNECT / INCLUDE_GOOGLE_CONNECT pattern used by FacebookImpl.m / GoogleConnectImpl.m. * IPhoneBuilder now flips both macros on (uncommenting the //#define lines added to CodenameOne_GLViewController.h) when the scanner sees com/codename1/io/oidc/* or com/codename1/social/AppleSignIn*. * Identity-stack CI's clang job now exercises BOTH configurations (stubs path AND full path) so a regression in either fires the gate. Also fixes two SpotBugs DM_DEFAULT_ENCODING violations: PkceChallenge and AppleSignIn's catch-block fallbacks for UnsupportedEncodingException were calling String.getBytes() without an explicit charset. Replaced with `throw new IllegalStateException(...)` since UTF-8 is guaranteed on every conforming JVM; the catch is dead code in practice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SpotBugs SBSC_USE_STRINGBUFFER_CONCATENATION flagged the `payloadB64 += "="` pad loop in decodeIdTokenClaims. Compute the pad count up front, build through a single StringBuilder. Functionally identical; OidcCoreTest 12/12 still passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* REC_CATCH_EXCEPTION at OidcTokens.decodeIdTokenClaims: the wide `catch (Exception e)` was catching only two declared possibilities (UnsupportedEncodingException from `new String(..., "UTF-8")` and IOException from JSONParser.parseJSON). Split into the two explicit catches; same behavior, no overcatch. * SIC_INNER_SHOULD_BE_STATIC_ANON at the Android OidcBrowserNativeImpl: the anonymous Runnable passed to runOnUiThread captured `this` only because it was anonymous, not because it needed instance state. Extracted to a static-nested LaunchBrowserRunnable that holds the Activity + URL explicitly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE at SystemBrowser.java:163. The `instanceof String` test on `src` two lines above already proves it non-null, so the subsequent `url == null` check is dead. Drop it. * SIC_INNER_SHOULD_BE_STATIC_ANON at MicrosoftConnect.java:107. The signIn() flow used three nested anonymous SuccessCallback instances; the outer one only needed method-locals (not the enclosing MicrosoftConnect this). Refactor the three callbacks into named static nested classes (DiscoveredCallback, AuthorizedCallback, ErrorCallback) that accept the host instance explicitly when needed for setAccessToken. 12/12 OidcCoreTest still passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Compared 110 screenshots: 110 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
…sites The identity-stack classes (OidcClient, AppleSignIn, *Connect, Auth0Connect) chain AsyncResource.ready(...) / .except(...) via anonymous SuccessCallback instances per the Codename One async idiom (the same pattern used by the existing Login / FaceBookAccess / Display classes). Many of those inner classes don't strictly need the enclosing-this capture, but rewriting each into a named static nested class bloats the call sites and obscures the control flow. Scope the exclusion narrowly to com.codename1.io.oidc.* and the explicit *Connect / AppleSignIn classes, mirroring the existing JavascriptContext precedent in the same file. Matches what the Android port's spotbugs-exclude.xml does for AndroidAsyncView and friends. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SpotBugs DLS_DEAD_LOCAL_STORE at AppleSignIn.java:235. The local `OidcConfiguration cfg = client.getConfiguration();` was a leftover debugging fetch -- never read in the body of the callback. Remove the assignment and the now-orphan import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR #5018's PMD scan flagged 66 unique violations across the new OIDC client and the related social/* sign-in wrappers. None of these are behavioural — they are all stylistic / lint-driven. - MissingOverride: added @OverRide on the anonymous-class and inner-class implementations of ConnectionRequest, SuccessCallback, Runnable, ActionListener, TokenStore, and AppleSignInCallback. - ControlStatementBraces: wrapped single-statement if/for bodies in `{}` across OidcClient, TokenStore, AppleSignIn and FirebaseAuth. - AvoidUsingVolatile: kept the `volatile` keyword on SystemBrowser.cachedNative/nativeProbed and AppleSignIn.CACHED_NATIVE/NATIVE_PROBED — these are the classic double-checked-locking guards, and removing volatile would let other threads observe a half-initialised reference. Annotated each with @SuppressWarnings("PMD.AvoidUsingVolatile") and a comment. - EmptyCatchBlock: replaced the trivial `// ignore` markers in TokenStore.load and OidcTokens.fromTokenResponse with comments that explain why a malformed expiry value is non-fatal. - LocalVariableNamingConventions / FormalParameterNamingConventions: renamed `native_` (the trailing-underscore dodge for the Java reserved word) to `provider` in SystemBrowser.authenticate / SystemBrowser.authenticateNative. - UnnecessaryModifier: stripped the redundant `public static` from TokenStore.DefaultStorageTokenStore — types nested in an interface are implicitly both. - UnnecessaryImport: removed the unused com.codename1.util.AsyncResource import from AppleSignIn. - LiteralsFirstInComparisons: flipped `error.equals("access_denied")` to `"access_denied".equals(error)` in OidcClient.handleRedirect. - ForLoopCanBeForeach: rewrote the index-only for-i loop in OidcClient.merge as an enhanced-for over the split pairs. OidcCoreTest still reports 12/12 green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The two `NumberFormatException nfe` catches in OidcTokens.fromTokenResponse and TokenStore.DefaultStorageTokenStore.load have explanatory comments but no statements -- PMD's EmptyCatchBlock rule only bypasses comment-only catches when the exception variable matches `^(ignored|expected)$`. Renamed both to `ignored`; comments preserved. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reformat the 22 compact `if (x) { return; }`-style blocks the prior
PMD round introduced in the OIDC + social modules into K&R-style
multi-line blocks. No behavior change; tests still 12/12.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Oauth2flow (now rejected by Google, Apple, Microsoft, Facebook) with a modern OpenID Connect client driven from the system browsercom.codename1.io.oidc.OidcClient(discovery + PKCE S256 + state/nonce + refresh + revoke + pluggableTokenStore) and aSystemBrowserfacade with native iOS (ASWebAuthenticationSession) and Android (Custom Tabs) implementationscom.codename1.social.AppleSignIn) with nativeASAuthorizationAppleIDProvideron iOS 13+ and OIDC web fallback elsewhereGoogleConnect/FacebookConnectwithsignIn(...)methods that go through the new stack, and adds newMicrosoftConnect(Entra ID),Auth0ConnectandFirebaseAuth(REST) wrappersOauth2with a migration recipeAuthenticationServices.framework+ injectscom.apple.developer.applesigninentitlement on iOS, and auto-injectsandroidx.browser:browseron Android, only when the scanner sees the new classes in useWhat's in this PR
Core (
CodenameOne/src/com/codename1/io/oidc/)OidcClient-- discover/authorize/refresh/revokeOidcConfiguration(builder + discovery-JSON parser)OidcTokens(with ID-token claim decoding andAccessTokenbridge)OidcException(typed errors:USER_CANCELLED,STATE_MISMATCH,NONCE_MISMATCH,DISCOVERY_FAILED, ...)PkceChallenge(RFC 7636 S256)TokenStore+DefaultStorageTokenStoreSystemBrowserfacade +OidcBrowserNativeNativeInterfaceProvider classes (
com.codename1.social)AppleSignIn+AppleSignInCallback+AppleSignInResult+AppleSignInNative(new)GoogleConnect#signIn(clientId, redirectUri, scopes...)(new method)FacebookConnect#signIn(appId, redirectUri, permissions...)(new method)MicrosoftConnect(new, multi-tenant Entra ID)Auth0Connect(new)FirebaseAuth(new, REST-based; email/password, IdP exchange, refresh)Native bindings
Ports/iOSPort/nativeSources/com_codename1_io_oidc_OidcBrowserNativeImpl.{h,m}--ASWebAuthenticationSessionPorts/iOSPort/nativeSources/com_codename1_social_AppleSignInNativeImpl.{h,m}--ASAuthorizationAppleIDProviderPorts/Android/src/com/codename1/io/oidc/OidcBrowserNativeImpl.java--androidx.browser.customtabswithACTION_VIEWfallbackPorts/Android/src/com/codename1/social/AppleSignInNativeImpl.java-- reportsisSupported=falseso AppleSignIn falls through to its OIDC web flowBuild plugin
IPhoneBuilderscanner addsusesOidc/usesAppleSignIn; auto-linksAuthenticationServices.frameworkand injects thecom.apple.developer.applesigninentitlement.AndroidGradleBuilderscanner addsusesOidc; auto-injectsandroidx.browser:browser:1.8.0(override withandroid.customTabsVersion).Docs & demo
docs/developer-guide/Authentication-And-Identity.asciidocchapter -- per-provider recipes, migration fromOauth2, iOSios.urlSchemeand Androidandroid.xintent_filtersetup.Samples/samples/UniversalSignInDemo/-- one button per provider plus a generic OIDC issuer, designed to be copy-pasteable.CI
.github/workflows/identity-stack.yml: path-filtered PR workflow with three jobs --linux-testsruns the identity unit tests, compiles the Maven plugin (verifies scanner edits), packages the Android port and asserts the new Java sources land inandroid_port_sources.jar, andjavacs the demo against built core.sample-secretsgreps the demo + identity sources for real-looking credentials (JWTs,AIza…keys, GitHub PATs, etc.).macos-clangrunsclang -fsyntax-onlyon the two iOS native sources.What this PR deliberately does not do
OidcClientjavadoc and in the new chapter; the remedy is to re-validate on your backend.GoogleConnect.doLogin()native iOS/Android paths. Those continue to work for code that depends on the existing native SDK integrations.Test plan
mvn -pl core-unittests -am test -P unittests -Dtest='OidcCoreTest,Oauth2*,*ConnectTest,Login*' -Dsurefire.failIfNoSpecifiedTests=false-- 30/30 passmvn -pl codenameone-maven-plugin compile(verifies scanner edits)mvn -pl android -am package -DskipTests--android_port_sources.jarcontains both new Java filesjavac -cp maven/core/target/classes Samples/samples/UniversalSignInDemo/UniversalSignInDemo.javaxcrun --sdk iphoneos clang -fsyntax-only ...on both new.mfilespython3 -c 'yaml.safe_load(...)'on the new workflowidentity-stackworkflow + the mainpr.ymlmatrix)UniversalSignInDemo)AppleSignIn-using build🤖 Generated with Claude Code