-
Notifications
You must be signed in to change notification settings - Fork 125
feat(mwa): add injectable auth token cache abstraction #284
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: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| using System.Threading.Tasks; | ||
|
|
||
| // ReSharper disable once CheckNamespace | ||
| namespace Solana.Unity.SDK | ||
| { | ||
| /// <summary> | ||
| /// Where the Mobile Wallet Adapter auth token gets stored. | ||
| /// | ||
| /// By default the SDK uses <see cref="PlayerPrefsAuthCache"/>, which | ||
| /// writes the token into <see cref="UnityEngine.PlayerPrefs"/>. That is | ||
| /// fine for most games but PlayerPrefs is plaintext on Android, so games | ||
| /// that hold real on-chain value should plug in a custom implementation | ||
| /// backed by Android Keystore / EncryptedSharedPreferences (or iOS | ||
| /// Keychain in the future). | ||
| /// | ||
| /// We only persist the auth token here. The public key is not a secret, | ||
| /// so it stays in PlayerPrefs and is out of scope for this interface. | ||
| /// | ||
| /// Implementations should keep all three methods cheap. <see cref="Clear"/> | ||
| /// is awaited synchronously from <c>Logout()</c>, so do not block on UI | ||
| /// or network calls inside it. | ||
| /// </summary> | ||
| public interface IMwaAuthCache | ||
| { | ||
| /// <summary> | ||
| /// Returns the stored auth token, or <c>null</c> if nothing is stored | ||
| /// yet. Return <c>null</c> (not an empty string) on a fresh install, | ||
| /// otherwise the SDK cannot tell "never logged in" from "logged in | ||
| /// with empty token". | ||
| /// </summary> | ||
| Task<string> Get(); | ||
|
|
||
| /// <summary> | ||
| /// Persists <paramref name="authToken"/>. Treat <c>null</c> or empty | ||
| /// input as a no-op so a stale callsite cannot wipe a valid session | ||
| /// by accident. | ||
| /// </summary> | ||
| Task Set(string authToken); | ||
|
|
||
| /// <summary> | ||
| /// Removes the stored token. Must be idempotent so calling it twice | ||
| /// (e.g. <c>Logout()</c> followed by <c>DisconnectWallet()</c>) does | ||
| /// not throw. | ||
| /// </summary> | ||
| Task Clear(); | ||
| } | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| using System.Threading.Tasks; | ||
| using UnityEngine; | ||
| using UnityEngine.Scripting; | ||
|
|
||
| // ReSharper disable once CheckNamespace | ||
| namespace Solana.Unity.SDK | ||
| { | ||
| /// <summary> | ||
| /// Default <see cref="IMwaAuthCache"/>, backed by | ||
| /// <see cref="PlayerPrefs"/>. Uses the same storage key | ||
| /// (<see cref="DefaultKey"/>) the SDK has been writing since PR #269, | ||
| /// so anyone who already has a cached session keeps it after upgrading. | ||
| /// No migration step needed. | ||
| /// | ||
| /// PlayerPrefs is plaintext on Android. Fine for hobby games and demos, | ||
| /// not fine for games that hold real assets - swap this out for a | ||
| /// custom <see cref="IMwaAuthCache"/> backed by Android Keystore or | ||
| /// EncryptedSharedPreferences in that case. | ||
| /// | ||
| /// The optional <paramref name="scope"/> on the second constructor lets | ||
| /// a single app keep independent sessions per wallet identity, e.g. | ||
| /// <c>new PlayerPrefsAuthCache("phantom")</c> writes to | ||
| /// <c>solana_sdk.mwa.auth_token.phantom</c>. Default (no scope) is | ||
| /// backward compatible with existing installs. | ||
| /// </summary> | ||
| [Preserve] | ||
| public class PlayerPrefsAuthCache : IMwaAuthCache | ||
| { | ||
| /// <summary> | ||
| /// Storage key used when no scope is supplied. Public so other | ||
| /// <see cref="IMwaAuthCache"/> implementations can reference the | ||
| /// same key for a one-time copy-up migration if they want to inherit | ||
| /// the existing PlayerPrefs session on first run. | ||
| /// </summary> | ||
| public const string DefaultKey = "solana_sdk.mwa.auth_token"; | ||
|
|
||
| private readonly string _key; | ||
|
|
||
| /// <summary> | ||
| /// Creates the default unscoped cache. Equivalent to | ||
| /// <c>new PlayerPrefsAuthCache(null)</c>. | ||
| /// </summary> | ||
| public PlayerPrefsAuthCache() : this(null) { } | ||
|
|
||
| /// <summary> | ||
| /// Creates a cache that namespaces its storage under | ||
| /// <c>{DefaultKey}.{scope}</c>. If <paramref name="scope"/> is null | ||
| /// or empty the default key is used, which keeps backward | ||
| /// compatibility with installs created before scoping existed. | ||
| /// </summary> | ||
| public PlayerPrefsAuthCache(string scope) | ||
| { | ||
| _key = string.IsNullOrEmpty(scope) | ||
| ? DefaultKey | ||
| : DefaultKey + "." + scope; | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public Task<string> Get() | ||
| { | ||
| string value = PlayerPrefs.GetString(_key, null); | ||
| return Task.FromResult(string.IsNullOrEmpty(value) ? null : value); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public Task Set(string authToken) | ||
| { | ||
| if (string.IsNullOrEmpty(authToken)) | ||
| { | ||
| return Task.CompletedTask; | ||
| } | ||
| PlayerPrefs.SetString(_key, authToken); | ||
| PlayerPrefs.Save(); | ||
| return Task.CompletedTask; | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| public Task Clear() | ||
| { | ||
| PlayerPrefs.DeleteKey(_key); | ||
| PlayerPrefs.Save(); | ||
| return Task.CompletedTask; | ||
| } | ||
| } | ||
| } |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.