Introduction
Welcome to SnapAuth!
This documentation is for our Client SDKs. You can browse our Server API documentation here.
Client SDKs are used to start browser/device-native WebAuthn flows. They return one-time-use tokens that you send to your backend to use with the Server APIs.
Setup
Installation
// Node
npm i --save '@snapauth/sdk'
// UMD
<script src="https://unpkg.com/@snapauth/sdk"></script>
XCode > File > Add Package Dependencies...
https://github.com/snapauthapp/sdk-swift
// Coming soon!
Client code should use the official SDKs, available from the platform's package manager.
We strictly follow semantic versioning, so you can upgrade confidently within a major version. It's strongly recommended to keep your SDK(s) up to date.
SDK Setup
// npm
import { SDK } from '@snapauth/sdk'
const snapAuth = new SDK('pubkey_xxxx')
// UMD
const snapAuth = new SnapAuth.SDK('pubkey_xxxx')
import SnapAuth
let snapAuth = SnapAuth(publishableKey: "pubkey_xxxx")
// Coming soon!
Client SDKs will use your publishable API key. You may safely embed it in whatever way is most convenient for you.
The publishable key is specific to an environment - i.e. a domain. This is a requirement for the WebAuthn protocol - Credentials are bound to a specific domain.
Registration
let name: string // Some value from your app. The user will see this during sign-in.
const registration = await snapAuth.startRegister({ name })
if (registration.ok) {
const token = registration.data.token
// Send token to your backend along with other relevant data
} else {
// If desired, inspect registration.error and decide how to proceed
}
// Tip: this needs to be in an `async` context. You can wrap this entire block
// in Task { ... } if you don't have one.
let result = await snapAuth.startRegister(name: "User's name")
switch result {
case .success(let registration):
// Send registration.token to your backend
case .failure(let error):
// If desired, inspect error and decide how to proceed
}
// Coming soon!
Wrapped Response (
registration.data
)
{
"token": string,
"expiresAt": Timestamp,
}
Registration is the process of creating a Credential for a User Account.
Users can have multiple Credentials, and each one must be registered independently.
To start the process of creating a Credential, use the SDK's startRegister
method.
On success, it will return a registration token
- this is a one-time-use token which you must send to your backend to attach to the user.
Request
Field | Type | Required? | Usage |
---|---|---|---|
name |
string |
yes | Name for the user. This is for client-side display; it is not sent to or retained by us. |
displayName |
string |
no | Display name for the user. Defaults to the value of name . Note: browser sign-in tends to display name and ignore displayName |
Response
Field | Type | Meaning |
---|---|---|
token |
string |
The one-time-use token to send to your server to finish credential registration |
expiresAt |
Timestamp |
When the token will expire. |
Authentication
// Note: use either id or handle, not both.
let id: string|null
let handle: string|null
const auth = await snapAuth.startAuth({ id, handle })
if (auth.ok) {
const token = auth.data.token
// Send token to your backend along with other relevant data
} else {
// If desired, inspect auth.error and decide how to proceed
}
// Tip: this needs to be in an `async` context. You can wrap this entire block
// in Task { ... } if you don't have one.
let result = await snapAuth.startAuth(.handle("Username"))
// Or snapAuth.startAuth(.id(userId))
switch result {
case .success(let auth):
// Send auth.token to your backend
case .failure(let error):
// If desired, inspect error and decide how to proceed
}
// Coming soon!
Wrapped Response (
auth.data
)
{
"token": string,
"expiresAt": Timestamp,
}
Authentication is the process of using a previously-registered credential. This may be to sign in to your application, or to elevate permissions for a sensitive action.
Like Registration, this API will return a one-time-use token which you must send to your backend to verify.
Request
Field | Type | Required? | Usage |
---|---|---|---|
id |
string |
no* | Your stable identifier for the user |
handle |
string |
no * | Your handle for the user (e.g. username) |
Response
Field | Type | Meaning |
---|---|---|
token |
string |
The one-time-use token to send to your server to verify |
expiresAt |
Timestamp |
When the token will expire. |
* You must provide either id
or handle
.
If you provide both, only id
will be used.
Autofill-assisted requests
import { AuthResponse } from '@snapauth/sdk'
const auth = await snapAuth.autofill()
if (auth.ok) {
const token = auth.data.token
// e.g.
const response = await postToYourBackend('/endpoint', { token })
// .. Do whatever you need to!
} else {
// Typically, autofill failures are safe to ignore
}
// On the page, you must also have a webauthn input:
<input type="text" autocomplete="username webauthn" />
// CAUTION: we've found the passkey AutoFill experience to be somewhat
// unreliable on supported Apple platforms. We've filed issues to get this fixed.
// Your UI must be displaying a TextField with `textContentType` of `.username`
import SnapAuth
struct MyView: View {
let snapAuth = SnapAuth(publishableKey: "pubkey_xxxx")
var body: some View {
TextField("Username")
.textContentType(.username)
.onAppear(perform: autoFill)
}
func autoFill() {
Task {
let autoFillResult = await snapAuth.handleAutoFill()
guard case .success(let authn) = autoFillResult else {
// Typically, autoFill failures are ok to ignore
return
}
// Send authn.token to your backend for verification
}
}
}
// Coming soon!
For devices and platforms that support it, you can set up autofill-assisted requests. This will prompt the user to authenticate without having to type anything in - they only have to approve the action.
There are two setup steps:
- Declare an input field as the autofill target.
- On web, this is done with
autocomplete="username webauthn"
, in addition to any other properties you have set. Normally this means addingwebauthn
to your existing username input field. - On Apple platforms, this is using the
textContentType
modifier of.username
to aTextField
(or equivalent).
- On web, this is done with
await
the result of calling theautofill()
method. If the user authenticates in this way, we'll return the same data you'd get from the user-initiated flow.
Different platforms handle autofill "failures" in various ways. This includes autofill failing due to a foreground request starting, platform unavailability, or various other reasons.
Tips
SDK Design
const result = await snapAuth.someAction(...)
// result has an `errors` property, containing 0 or more errors
if (result.ok) {
// result has a `data` property, which contains the documented response
} else {
// `errors` is guaranteed to have at least one entry
}
let result = await snapAuth.someAction(...)
switch result {
case .success(let data):
// data is the documented response format as a platform-native Struct
case .failure(let error):
// Look at the error code and decide how to proceed
}
// Coming soon!
We aim to keep our SDKs as platform-native as possible. As such, we do not auto-generate SDKs from API definitions. Our documentation tooling doesn't perfectly support some of the subtle platform-specific differences - we're working on improving this!
The SDKs are designed so that following the "happy path" will be most reliable.
We also focus on type safety so that you can leverage the tooling you're already familiar with.
Responses use a Result
type (natively, where it exists) to indicate success or failure.
You should never need to run SnapAuth Client SDKs with try/catch blocks.
Token Exchange
The Client SDKs return a one-time-use token, which you must be sent to your backend for further use. These tokens are exchanged with our Server SDKs to complete the registration and authentication processes.
Defensive Coding
Results may contain errors
even if they are ok
.
These could hint at an outdated SDK, incorrect usage, deprecation, or something else.
Browser Usage
Browsers require the WebAuthn APIs to be used only in response to a user gesture, such as onClick or onSubmit.
Native apps tend to not have this restriction, but it's a good idea to avoid surprising your users!
API Format
Types
Timestamp
integer
:
Unix Timestamp, in seconds
Our Swift SDK automatically translates this to a native Date
object.
Error
object
:
Information about an error or warning
Field | Type | Meaning |
---|---|---|
code |
string |
A machine-readable code. See below |
message |
string |
A human-readable description of the error. This is intended primary for developers, not end-users |
Errors
Code | Meaning |
---|---|
timeout |
The user did not register or authenticate quickly enough |
network_error |
There was a connection error between the client and SnapAuth |
bad_request |
An invalid request was made. Usually this is a missing or invalid field |
server_error |
Something went wrong our end. Please try again momentarily |
canceled_by_user |
The user canceled the registration or authentication request |
invalid_domain |
You are trying to perform a request on a domain that doesn't match the API key; OR you're trying to run on a non-HTTPS page |
unexpected |
An unexpected error occurred. Please contact us for help! |