Interface IAccountProvider
- Namespace
- VpnHood.AppLib.Abstractions.Accounts
- Assembly
- VpnHood.AppLib.Abstractions.dll
Provides the app's account backend: the sign-in, the signed-in person's account and its premium code, the products the app may sell, and the account's deletion.
public interface IAccountProvider
Properties
AuthenticationProvider
Gets the sign-in: who is signed in, and the credential that proves it.
IAuthenticationProvider AuthenticationProvider { get; }
Property Value
Billing
Null when the app has no in-app purchasing.
AppBilling? Billing { get; }
Property Value
Methods
DeleteAccount(CancellationToken)
Delete the signed-in account everywhere: the backend erases the person, every device is signed out, and a later sign-in creates a brand-new account. Nothing blocks it — website billing is cancelled at the end of its paid period backend-side, and a store subscription is deliberately left untouched (signing in again brings it back by itself; only the person can cancel it, in their store). It never ends already-paid access. Mandatory, not optional: any provider with sign-in owes an in-app deletion path by store policy (Apple 5.1.1(v), Google Play).
The backend call only — no ui context, because there is nothing to show. Signing THIS device out belongs to the app, which does it after this returns, so a refusal leaves the session intact and the person can retry.
Task DeleteAccount(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that completes once the backend has deleted the account.
Exceptions
- ApiException
The account backend refused the request.
- OperationCanceledException
cancellationTokenwas canceled.
GetAccount(CancellationToken)
The signed-in account, or null. AccessCodeInfo carries THE one code serving this account, whichever channel delivered it: the backend ranks everything the account holds and recomputes the winner on every read, so the app never sees a list and never has to pick. Subscription says whether a store subscription is what is paying right now. A displayed expiry is advisory; only an access-server refusal ends use of the credential.
Task<Account?> GetAccount(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
Exceptions
- ApiException
The account backend refused the request.
- OperationCanceledException
cancellationTokenwas canceled.
GetProductIds(CancellationToken)
The store product ids this app may sell — the backend's answer, because no store can list an app's own catalog: StoreKit and Play Billing only price ids they are given, so the ids must come from the side that redeems them. Provider prices exactly this list. Empty when nothing is sellable.
Task<IReadOnlyList<string>> GetProductIds(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task<IReadOnlyList<string>>
A task whose result is the product IDs.
Exceptions
- ApiException
The account backend refused the request.
- InvalidOperationException
The app has no store, so it sells nothing.
- OperationCanceledException
cancellationTokenwas canceled.
ReportAccessCodeRejected(string, CancellationToken)
Tell the account that the access server REFUSED its current code — the only thing a device ever reports, and the whole of what the backend's ranking needs. Every code an account holds is either eligible or rejected: no expiry dates, no reasons, no observation times, and no successful-connection counterpart to this call.
The backend applies it only while this is still the account's CURRENT code, so a delayed refusal that has been overtaken by a different one changes nothing. It is recorded against THIS account alone — the same bearer code may be serving somebody else perfectly well — and against every entry holding that string, because identical codes are the same credential.
Rejection skips a code, it never deletes one, and it never skips a code being paid for right now: that one is still served, and the refusal is only recorded. Where every code the account holds has been refused, the account keeps serving the one refused last. Uploading the code again through SetAccessCode(string?, CancellationToken) clears its refusal, which is all that Retry needs to be — and the reason the one edge case here is survivable: remove-then-re-add of the same string can be caught by a late report, and one more retry undoes it.
The code travels in the authenticated request body and never in a URL.
Task ReportAccessCodeRejected(string accessCode, CancellationToken cancellationToken)
Parameters
accessCodestringThe code the access server refused.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- ApiException
The account backend refused the request.
- NotSupportedException
The provider keeps no codes on the account.
- OperationCanceledException
cancellationTokenwas canceled.
SetAccessCode(string?, CancellationToken)
Upload a code the person typed into the account's ONE upload slot, or empty the slot with null. The uploaded code ranks above every code of the account that nobody is paying for, because typing a code is saying use this, and below anything being paid for right now.
The backend takes the code ON TRUST: it stores any string of valid access-code shape without proving that the code exists, because only the access server can say whether a code works and it says so at use time. There is therefore no not found answer to inspect.
Uploading transfers the code to the account, and the account does not hand it back when it is deleted. Every upload replaces the slot's code, a code the account already holds too, and clears a refusal reported for that code (ReportAccessCodeRejected(string, CancellationToken)).
Nothing here is ordered by time. Two devices that both upload are resolved by whichever call reaches the backend last, so a code typed during an outage can win over a later decision made elsewhere — accepted deliberately, because ordering it would cost a whole sync protocol to settle an argument almost nobody is having.
Task SetAccessCode(string? accessCode, CancellationToken cancellationToken)
Parameters
accessCodestringThe code to upload, or null to idempotently empty the slot.
cancellationTokenCancellationTokenThe token to monitor for cancellation requests.
Returns
- Task
A task that represents the asynchronous operation.
Exceptions
- ApiException
The account backend refused the request, as the portal refuses a string that is not shaped as a code.
- NotSupportedException
The provider keeps no codes on the account.
- OperationCanceledException
cancellationTokenwas canceled.