loading
Note:
The SMS has to opt in Only a message whose last line names this exact origin is ever offered, and only the code after the # is read:
text
Your Butil code is 123456

@example.com #123456
Get that line wrong - the wrong origin, a missing # - and the browser simply never offers the message. Nothing else about it is inspected.
Warning:
Chromium on Android, over HTTPS No other engine implements it, and it is autofill rather than a replacement for typing: keep the input usable, start the wait as the code step appears (the browser shows a prompt while one is pending), and let a timeout end it.

Support check

IsSupported

Returns true when the runtime exposes window.OTPCredential. During prerender/SSR the check returns false rather than throwing, so defer it to OnAfterRenderAsync.

C#
@inject Bit.Butil.WebOtp webOtp

var supported = await webOtp.IsSupported();
Live sample
support check output
Results will appear here when you interact with the samples.

Wait for a code

Receive / Abort

Waits for a matching SMS and returns the code from it, or null when the wait was aborted, timed out, or the user dismissed the prompt. Cancelling the CancellationToken ends it the same way Abort does - which is what to hook a disposed component up to.

@implements IDisposable
@inject Bit.Butil.WebOtp webOtp

@* autocomplete="one-time-code" is what lets the platform's own keyboard suggestion work too, so the
   input stays usable when the API is not there - which on every engine but Chromium is always. *@
<input @bind="_otp" autocomplete="one-time-code" inputmode="numeric" />

@code {
    private string? _otp;
    private readonly CancellationTokenSource _cts = new();

    // Started as the code step appears, not on page load: the browser shows a prompt of its own
    // while a wait is pending.
    private async Task Wait()
    {
        var code = await webOtp.Receive(TimeSpan.FromSeconds(60), _cts.Token);

        // null when the wait was aborted, timed out, or the user dismissed the prompt.
        if (code is not null)
        {
            _otp = code;   // fill the input and submit
            StateHasChanged();
        }
    }

    // Cancelling the token ends the wait the same way Abort does, which is what to hook a disposed
    // component up to.
    public void Dispose() => _cts.Cancel();
}
Live sample
Timeout (seconds, 0 = wait indefinitely)
Code
web otp output
Results will appear here when you interact with the samples.

API reference

Member
Signature
Description
IsSupported
ValueTask<bool> IsSupported()
True when the runtime exposes window.OTPCredential. Returns default (false) during prerender/SSR instead of throwing.
Receive
ValueTask<string?> Receive(TimeSpan? timeout = null, CancellationToken cancellationToken = default)
Waits for a matching SMS and returns its code. Null when aborted, timed out, or dismissed. One wait per service instance.
Abort
ValueTask<bool> Abort()
Ends the wait started on this instance - the user chose to type the code instead. False when nothing was pending.
An unhandled error has occurred. Reload 🗙