Go server-side library for WebAuthn registration and verification.
go get github.com/spiretechnology/go-webauthn
When users register credentials, they need to be stored somewhere. You can store credentials anywhere you'd like, as long as you implement the webauthn.CredentialStore
interface.
type myCredentialStore struct {}
func (s *myCredentialStore) GetCredentials(ctx context.Context, user webauthn.User) ([]webauthn.Credential, error) {
// ...
}
func (s *myCredentialStore) GetCredential(ctx context.Context, user webauthn.User, credentialID []byte) (*webauthn.Credential, error) {
// ...
}
func (s *myCredentialStore) StoreCredential(ctx context.Context, user webauthn.User, credential webauthn.Credential, meta webauthn.CredentialMeta) error {
// ...
}
Make sure to store all the fields provided in the webauthn.Credential
struct in your database: ID
, Type
, PublicKey
, and PublicKeyAlg
.
Create a WebAuthn instance with your relying party information and credential store.
wa := webauthn.New(webauthn.Options{
RP: webauthn.RelyingParty{ID: "mycompany.com", Name: "My Company"},
Credentials: &myCredentialStore{},
})
When a user sends a request to register a WebAuthn credential, you'll need to generate a registration challenge and send it to the client. You can do this with the CreateRegistration
method.
// Create a registration challenge
challenge, err := wa.CreateRegistration(ctx, webauthn.User{
ID: "123", // Unique ID for the user (eg. database ID)
Name: "johndoe", // Username or email
DisplayName: "John Doe", // User's full name
})
// Marshal `challenge` to JSON and send it to the client
// ...
The challenge
value returned is JSON-serializable and can be sent to the client without any additional processing or encoding.
When the user sends back a response to the registration challenge, you can verify it with the VerifyRegistration
method.
// Unmarshal the user's response from JSON
var response webauthn.RegistrationResponse
// ...
// Verify the registration response
user := webauthn.User{ /* same user as before */ }
result, err := wa.VerifyRegistration(ctx, user, response)
When a user sends a request to authenticate with a WebAuthn credential, you'll generate an authentication challenge and send it to the client. You can do this with the CreateAuthentication
method.
// Create an authentication challenge
user := webauthn.User{ /* same user as before */ }
challenge, err := wa.CreateAuthentication(ctx, user)
// Marshal `challenge` to JSON and send it to the client
// ...
When the user sends back a response to the authentication challenge, you can verify it with the VerifyAuthentication
method.
// Unmarshal the user's response from JSON
var response webauthn.AuthenticationResponse
// ...
// Verify the authentication response
user := webauthn.User{ /* same user as before */ }
result, err := wa.AuthenticationRegistration(ctx, user, response)
For both registration and authentication, the client is responsible for requesting challenges from the server, and responding to those challenges.
We recommend using our js-webauthn library to handle the client-side flow for you. That library is designed to work with this one.
For a full example of both the server and client flow, run the example project in this repo:
go run ./example
Then open http://localhost:4000 in your browser.