MCPcopy Index your code
hub / github.com/egregors/passkey

github.com/egregors/passkey @v1.0.8

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.0.8 ↗ · + Follow
261 symbols 722 edges 21 files 90 documented · 34% 5 cross-repo links
What it actually does AI analysis from the code graph — generated when you open this
loading…
README
<h1>🔑 passkey</h1>

passkey is a Go library for implementing WebAuthn services

Build Status Go Report Card Coverage Status godoc


Table of Contents

Features

  • User Management: Handle user information and credentials.
  • WebAuthn Integration: Easily integrate with WebAuthn for authentication.
  • Session Management: Manage user sessions securely.
  • Middleware Support: Middleware for authenticated routes.

[!NOTE] In general, this library is built on top of two open-source solutions: * Golang WebAuthn Library – https://github.com/go-webauthn/webauthn * JS/TS SimpleWebAuthn client – https://github.com/MasterKale/SimpleWebAuthn

Used in project: Static Badge Static Badge

Actual versions: GitHub Release GitHub Release

Installation

To get started, you need to have Go installed on your machine. If you don't have it installed, you can download it from here.

Install the library using go get:

go get github.com/egregors/passkey

Usage

Library Usage

To add a passkey service to your application, you need to do a few simple steps:

Implement storages: UserStore and two SessionStore one for WebAuthn and one for general session

package passkey

import "github.com/go-webauthn/webauthn/webauthn"

// UserStore is a persistent storage for users and credentials
type UserStore interface {
    Create(username string) (User, error)
    Update(User) error

    Get(userID []byte) (User, error)
    GetByName(username string) (User, error)
}

// SessionStore is a storage for session data
type SessionStore[T webauthn.SessionData | UserSessionData] interface {
    Create(data T) (string, error)
    Delete(token string)

    Get(token string) (*T, bool)
}

Your User model also should implement User interface:

package main

import "github.com/go-webauthn/webauthn/webauthn"

// User is a user with webauthn credentials
type User interface {
    webauthn.User
    PutCredential(webauthn.Credential)
}

This interface is an extension of the webauthn.User interface. It adds a PutCredential method that allows you to store a credential in the user object.

Create a new Passkey instance and mount the routes

The whole example is in _example directory.

package main

import (
    "embed"
    "fmt"
    "io/fs"
    "net/http"
    "net/url"
    "time"

    "github.com/go-webauthn/webauthn/webauthn"

    "github.com/egregors/passkey"
    "github.com/egregors/passkey/log"
)

//go:embed web/*
var webFiles embed.FS

const userKey passkey.AuthUserIDKey = "pkUser"

func main() {
    proto := getEnv("PROTO", "http")             // "http" | "https"
    sub := getEnv("SUB", "")                     // "" | "login."
    host := getEnv("HOST", "localhost")          // "localhost" | "example.com"
    originPort := getEnv("ORIGIN_PORT", ":8080") // ":8080" | "" if you use reverse proxy it should be the most "external" port
    serverPort := getEnv("SERVER_PORT", ":8080") // ":8080"

    origin := fmt.Sprintf("%s://%s%s%s", proto, sub, host, originPort)

    l := log.NewLogger()

    pkey, err := passkey.New(
        passkey.Config{
            WebauthnConfig: &webauthn.Config{
                RPDisplayName: "Passkey Example", // Display Name for your site
                RPID:          host,              // Generally the FQDN for your site
                RPOrigins:     []string{origin},  // The origin URLs allowed for WebAuthn
            },
            UserStore:        NewUserStore(),
            AuthSessionStore: NewSessionStore[webauthn.SessionData](),
            UserSessionStore: NewSessionStore[passkey.UserSessionData](),
        },
        passkey.WithLogger(l),
        passkey.WithUserSessionMaxAge(60*time.Minute),
        passkey.WithSessionCookieNamePrefix("passkeyDemo"),
        passkey.WithInsecureCookie(), // In order to support Safari on localhost. Do not use in production.
    )
    if err != nil {
        panic(err)
    }

    mux := http.NewServeMux()

    // mount the passkey routes
    pkey.MountRoutes(mux, "/api/")

    // public routes
    web, _ := fs.Sub(webFiles, "web")
    mux.Handle("/", http.FileServer(http.FS(web)))
    mux.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) {
        pkey.Logout(w, r)
        http.Redirect(w, r, "/", http.StatusSeeOther)
    })

    // private routes
    privateMux := http.NewServeMux()
    privateMux.HandleFunc("/", privateHandler())

    // wrap the privateMux with the Auth middleware
    withAuth := pkey.Auth(
        userKey,
        nil,
        passkey.RedirectUnauthorized(url.URL{Path: "/"}),
    )
    mux.Handle("/private", withAuth(privateMux))

    // start the server
    l.Infof("Listening on %s\n", origin)
    if err := http.ListenAndServe(serverPort, mux); err != nil { //nolint:gosec
        panic(err)
    }
}

You can optionally provide a logger to the New function using the WithLogger option.

Full list of options:

Name Default Description
WithLogger NullLogger Provide custom logger
WithInsecureCookie Disabled (cookie is secure by default) Sets Cookie.Secure to false
WithSessionCookieNamePrefix pk Sets the name prefix of the session and user cookies
WithUserSessionMaxAge 60 minutes Sets the max age of the user session cookie

Client-side

You need a client-side library that can be used to interact with the server-side library. In example app we use SimpleWebAuthn library (check _example/web directory).

Example Application

The library comes with an example application that demonstrates how to use it. To run the example application just run the following command:

# go run local example app on http://localhost:8080 (no ssl)
make run 

or

# run example app in docker container on https://localhost (with self-signed certificate)
make up 

API

Method Description
New(cfg Config, opts ...Option) (*Passkey, error) Creates a new Passkey instance.
MountRoutes(mux *http.ServeMux, path string) Mounts the Passkey routes onto a given HTTP multiplexer.
Auth(userIDKey string, onSuccess, onFail http.HandlerFunc) func(next http.Handler) http.Handler Middleware to protect routes that require authentication.
UserIDFromCtx(ctx context.Context, pkUserKey string) ([]byte, bool) Returns the user ID from the request context. If the userID is not found, it returns nil and false.

Middleware

The library provides a middleware function that can be used to protect routes that require authentication.

Auth(userIDKey string, onSuccess, onFail http.HandlerFunc) func (next http.Handler) http.Handler

It takes key for context and two callback functions that are called when the user is authenticated or not. You can use the context key to retrieve the authenticated userID from the request context with passkey.UserIDFromCtx.

passkey contains a helper function:

Helper Description
Unauthorized Returns a 401 Unauthorized response when the user is not authenticated.
RedirectUnauthorized(target) Redirects the user to a given URL when they are not authenticated.
UserFromContext Get userID from context

You can use it to protect routes that require authentication:

package main

import (
    "net/url"

    "github.com/egregors/passkey"
)

func main() {
    pkey, err := passkey.New(...)
    check(err)

    withAuth := pkey.Auth(
        "pkUser",
        nil,
        passkey.RedirectUnauthorized(url.URL{Path: "/"}),
    )

    mux.Handle("/private", withAuth(privateMux))
}

Development

Common tasks

To common dev task just use make:

➜  passkey git:(main) make help
Usage: make [task]

task                 help
------               ----

lint                 Lint the files
test                 Run unittests
run                  Run example project
up                   Run example project with local SSL (self-signed certificate)
gen                  Generate mocks
update-go-deps       Updating Go dependencies

help                 Show help message

Mocks

Use mockery to generate mocks for interfaces.A

Troubleshooting

FAQ

Q: I'm getting an error "named cookie not present" when I try to interact with passkey running on localhost from macOS Safari.

A: This is a known issue with Safari and localhost. You can work around it by using the WithInsecureCookie option when creating a new Passkey instance. This will set the Secure flag on the session cookie to false.

Q: I'm getting an error "Error validating origin" when I try to interact with passkey.

A: This error occurs when the origin URL is not included in the RPOrigins list. Make sure that the origin URL is included in the RPOrigins list when creating a new Passkey instance.

Q: I'm getting an error "WebAuthn in not supported in this browser" when I try to interact with passkey on localhost from iOS Safari.

A: Mobile Safari no not store insecure cookies. To play around with the example app on iOS Safari, you should run in with self-signed certificate. Use make up to run the example app in docker container with self-signed certificate.

Contributing

Bug reports, bug fixes and new features are always welcome. Please open issues and submit pull requests for any new code.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Extension points exported contracts — how you extend this code

Logger (Interface)
Logger is a simple logger interface [3 implementers]
ifaces.go
WebAuthnInterface (Interface)
(no doc) [2 implementers]
deps/deps.go
Option (FuncType)
(no doc)
options.go
User (Interface)
User is a user with webauthn credentials [3 implementers]
ifaces.go
UserStore (Interface)
UserStore is a persistent storage for users and credentials [3 implementers]
ifaces.go
SessionStore (Interface)
SessionStore is a storage for session data
ifaces.go

Core symbols most depended-on inside this repo

Get
called by 94
ifaces.go
Return
called by 68
mock_Logger.go
Debugf
called by 50
ifaces.go
Errorf
called by 34
ifaces.go
EXPECT
called by 29
mock_User.go
NewMockUserStore
called by 28
mock_UserStore.go
On
called by 28
log/log.go
EXPECT
called by 24
mock_UserStore.go

Shape

Method 161
Function 50
Struct 39
Interface 5
Class 4
FuncType 1
TypeAlias 1

Languages

Go93%
TypeScript7%

Modules by API surface

mock_User.go34 symbols
mock_UserStore.go28 symbols
mock_Logger.go28 symbols
deps/mock_WebAuthnInterface.go28 symbols
mock_SessionStore.go22 symbols
ifaces.go16 symbols
_example/web/index.es5.umd.min.js16 symbols
passkey.go14 symbols
_example/storage.go13 symbols
handlers.go11 symbols
log/log.go8 symbols
options.go6 symbols

For agents

$ claude mcp add passkey \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact