Skip to main content

Profiles

PureProfilesPlugin is a native login-profile manager registered in runelite-plugin.properties (config group n3profiles, disabled by default). Rebuilt 2026-05-31 on a unified, typed, encrypted credential store; the previous JCEF/decompiled scaffolding was removed.

Architecture

  • PureProfilesPlugin - lifecycle + nav button (blank navIcon() fallback).
  • PureProfilesPanel - login-screen profile list, add/delete, unlock, paste-based bulk import, Jagex OAuth trigger.
  • PureProfilesConfig - config group n3profiles (autoLog, unlockPassword, profilesData, salt).
  • ProfileStore - shared encrypted store (see below).
  • StoredProfile - one typed credential line.
  • ProfileStorageCodec - AES-GCM v2 encode/decode + legacy migration.
  • ProfileLoginService - applies a profile to the client (legacy fields or Jagex reflection).
  • JagexLoginAdapter / ReflectionJagexLoginAdapter / UnsupportedJagexLoginAdapter - revision-sensitive obfuscated-name login hooks behind a testable boundary.
  • jagexauth/ - system-browser OAuth (Desktop.browse + local HttpServer): JagexAuth, AuthFlow, CallbackHandler, OAuthReceiver, JsonParser.

Typed credential store

ProfileStore (@Inject ConfigManager) reads/writes the n3profiles group (profilesData + salt), deriving an AES key from the unlock password via PBKDF2WithHmacSHA256 (100k iterations, 128-bit) wrapped as an AES SecretKeySpec. Because its only dependency is ConfigManager (present in every plugin injector), other plugins - notably the Break Handler - can inject their own instance and read the same blob without a cross-plugin singleton.

StoredProfile lines are typed:

LEGACY:label:login:password:pin
JAGEX:displayName:characterId:sessionId:userHash

Pre-existing untyped lines (label:login:password[:pin]) decode as LEGACY for backward compatibility; ProfileStorageCodec re-encodes them on first load (isMigrationRequired).

API

  • load(char[] password) - decrypt + migrate + parse to List<StoredProfile> (throws on bad password).
  • save(List<StoredProfile>, char[] password) - re-encrypt and persist.
  • loadWithConfiguredPassword() - decrypt with n3profiles.unlockPassword; empty list on absence/failure, never throws.
  • hasConfiguredPassword() - whether an auto-unlock password is set.

StoredProfile and its Kind/getters are public so cross-package consumers can read profiles; the factories/parse/format stay package-private to n3Profiles.

Bulk import

Unlocked Profiles panels expose Bulk Import for multiline paste input. It accepts only typed rows:

LEGACY:label:login:password[:pin]
JAGEX:label:characterId:sessionId[:userHash]

Blank lines are ignored. Blank labels default to the legacy login or Jagex character ID. Legacy rows require login and password, and the optional PIN must be blank or exactly four digits. Jagex rows require character ID and session ID; user hash remains optional. Malformed rows and duplicate labels, including duplicates already stored, are skipped and reported in the import summary.

Login

ProfileLoginService:

  • applyLegacyProfile(username, password, autoLogin) - resets reflected Jagex login state back to the username/password form, sets client username/password, and when autoLogin and on the login screen, dispatches Enter keys.
  • applyJagexProfile(sessionId, characterId, displayName) - applies a Jagex session via the reflection adapter, keeps login index 10, and attempts the shared welcome-screen continue helper when available (throws ReflectiveOperationException if hooks unavailable).
  • isJagexLoginAvailable() - adapter support probe.

Jagex login hooks use the current mappings in ObfuscatedNames (xo.ah, et.ln, lo.lx, and bx.cl, with byte garbage 0). These names are revision-sensitive; see the profiles notes in N3PLUGINS_SOURCE_OF_TRUTH.md.

Testing

.\gradlew.bat test --tests com.n3plugins.n3Profiles.* --console plain
  • ProfileStoreTest - encode/decode round-trip, configured-password load, salt creation (map-backed ConfigManager mock).
  • StoredProfileTest - typed/untyped parse, format, parseAll.
  • BulkProfileImportTest - strict typed paste rows, optional fields, invalid rows, duplicate skipping, and partial summaries.
  • ProfileStorageCodecTest - AES-GCM v2 + legacy ¬ migration.
  • ReflectionJagexLoginAdapterTest - adapter resolution under the default-package constraint.
  • jagexauth/JsonParserTest - OAuth token JSON parsing.

Live validation (pending)

Jagex OAuth round-trip and reflection login on rev239; legacy auto-login key dispatch; end-to-end re-login from the Break Handler PROFILES mode.