Skip to content

Add info() to fetch the authenticated record user#190

Open
welpie21 wants to merge 3 commits into
surrealdb:mainfrom
welpie21:feature/info-auth
Open

Add info() to fetch the authenticated record user#190
welpie21 wants to merge 3 commits into
surrealdb:mainfrom
welpie21:feature/info-auth

Conversation

@welpie21

Copy link
Copy Markdown

The gap

The Java SDK had no way to fetch the currently-authenticated record user. The JS SDK (golden reference) exposes this as auth() (called info() in the v1 JS SDK), implemented as SELECT * FROM ONLY $auth; SurrealDB also exposes it as the info RPC method. Java users had to hand-write the $auth query.

Reproduction

On the embedded memory engine (server v3.x semantics), with a record access and a user table readable by its own record user:

DEFINE TABLE user SCHEMALESS PERMISSIONS FOR select WHERE id = $auth.id;
DEFINE ACCESS user ON DATABASE TYPE RECORD
    SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
    SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
    DURATION FOR TOKEN 15m, FOR SESSION 12h;

After signup(new RecordCredential(...)):

  • query("SELECT * FROM ONLY $auth").take(0) returns the record object ({ email, id, pass }) — the underlying capability works.
  • Unauthenticated / after invalidate(), the ONLY form throws InternalException: Expected a single result output when using the ONLY keyword (because $auth is NONE), while the plain SELECT * FROM $auth returns an empty array in both states and [record] when authenticated.

The fix

  • Surreal.info() returning Optional<Value> and Surreal.info(Class<T>) returning Optional<T>, following the select(RecordId) / select(Class, RecordId) conventions.
  • Implemented as a native JNI method (Java_com_surrealdb_Surreal_info in src/main/rust/surreal.rs) mirroring selectRecordId: it runs SELECT * FROM $auth via the shared surrealdb_query helper and returns 0 for an empty result (mapped to Optional.empty()). This matches how the codebase structures such accessors (run(), selectRecordId are native query-formatting methods) rather than composing Java-side query() calls.
  • The non-ONLY form is used deliberately so an unauthenticated or invalidated session cleanly yields Optional.empty() instead of the ONLY-keyword error, while returning the same record as the JS SDK's SELECT * FROM ONLY $auth when authenticated.

Tests

New InfoTests (memory engine, patterned after CredentialTests), plus a small User test POJO:

  • info_afterSignup_returnsAuthenticatedRecord — signup then info() returns the record with the expected email, and its id equals RETURN $auth.
  • info_typed_hydratesPojoinfo(User.class) hydrates a POJO (RecordId id, String email).
  • info_unauthenticated_returnsEmpty — both overloads return Optional.empty().
  • info_afterInvalidate_returnsEmpty — present after signup, empty after invalidate().

Full suite: 337 tests, 0 failures.

🤖 Generated with Claude Code

welpie21 and others added 2 commits July 10, 2026 14:48
Adds Surreal.info() and Surreal.info(Class<T>) returning the record of
the currently-authenticated record user (the record pointed to by the
$auth parameter), for parity with the JS SDK's auth() (info() in the v1
JS SDK) and the info RPC method.

Implemented as a native JNI method mirroring selectRecordId: it runs
SELECT * FROM $auth (deliberately without ONLY, so an unauthenticated
session yields an empty array mapped to Optional.empty() instead of the
"Expected a single result output when using the ONLY keyword" error)
and returns the single record when present.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@welpie21 welpie21 marked this pull request as ready for review July 10, 2026 14:27

@emmanuel-keller emmanuel-keller left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — approve with nits.

Clean and faithful to the existing select(RecordId) pattern on both sides. I checked the failure modes: get_surreal_ref! (not the cloning variant) is used so $auth resolves on the real session; unauthenticated / system-user (root/ns/db) sessions yield []Optional.empty() on both sides; there's no unwrap/expect and all error paths route through the exception sink; the returned Value is owned/cleaned up exactly like select.

Two points:

  1. Version: build.gradle is still 2.1.2, which is already released (commit bc3812c). This net-new public API needs a minor bump (e.g. 2.2.0-SNAPSHOT) — but that's really a batch decision across the currently-open PRs, not just this one.
  2. Test gap: no explicit system-user (root/ns/db) case asserting empty — low risk since info_unauthenticated_returnsEmpty hits the same $auth=NONE path, but worth adding (use a ns/db user, since the memory engine may reject root sign-in).

*
* @return an Optional containing the authenticated record user's Value, or an
* empty Optional when the session is not authenticated as a record user
* (e.g. unauthenticated, invalidated, or a system user) or the record

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Javadoc says empty is also returned when "the record is not visible to it" (permissions). That's plausible but untested and server-version-dependent — the only permissions case covered is the permissive WHERE id = $auth.id. Consider softening the wording or adding a test.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test rather than softening the wording: info_recordNotVisibleToUser_returnsEmpty (8e375e8) signs up a record user against a table defined with PERMISSIONS FOR select NONE, asserts the session is still authenticated (RETURN $auth yields a user record id), and then asserts both info() and info(Class) return an empty Optional. So the behavior matches the Javadoc on the embedded engine (SurrealDB 3.1.5): SELECT * FROM $auth returns an empty result set when permissions hide the record, which info() maps to Optional.empty(). Kept the Javadoc as-is now that the claim is pinned by a test.

Review follow-up: the info() Javadoc claims an empty Optional is also
returned when table permissions hide the record from the authenticated
user, but only the permissive `WHERE id = $auth.id` case was covered.

Adds a test signing up a record user against a table defined with
`PERMISSIONS FOR select NONE` and asserts that, while the session is
still authenticated ($auth is set), info() and info(Class) both return
empty — confirming the documented behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants