Yet another ykman Go lib for requesting OATH TOTP Multi-Factor Authentication Codes from Yubikey Devices

Overview


Got

ykmangoath
Ykman OATH TOTP with Go

Yet another ykman Go lib for requesting OATH TOTP Multi-Factor Authentication Codes from Yubikey Devices.



🚧 Work-in-Progress

There are already some packages out there which already wrap Yubikey CLI for Go to manage OATH TOTP, but they lack all or some of the following features:

  • Go Context support (handy for timeouts/cancellation etc)
  • Multiple Yubikeys (identified by Device Serial Number)
  • Password protected Yubikey OATH applications

Hence, this package, which covers those features! Big thanks to joshdk/ykmango and 99designs/aws-vault as they heavily influenced this library. Also this library is somewhat based on the previous implementation of Yubikey support in aripalo/vegas-credentials (which this partly replaces in near future).

This library supports only a small subset of features of Yubikeys & OATH account management, this is by design.


Installation

Requirements

  • Yubikey Series 5 device (or newer with a OATH TOTP support)
  • ykman Yubikey Manager CLI as a runtime dependency
  • Go 1.18 or newer (for development)

Get it

go get -u github.com/aripalo/ykmangoath

Getting Started

This ykmangoath library provides a struct OathAccounts which represents a the main functionality of Yubikey OATH accounts (via ykman CLI). You can “create an instance” of the struct with ykmangoath.New and provide the following:

  • Context (type of context.Context) which allows you to implement things like cancellations and timeouts
  • Device Serial Number which is the 8+ digit serial number of your Yubikey device which you can find:
    • printed in the back of your physical Yubikey device
    • by running command ykman info in your terminal
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/aripalo/ykmangoath"
)

func main() {
	myTimeout := 20*time.Second
	ctx, cancel := context.WithTimeout(context.Background(), myTimeout)
	defer cancel()

	deviceSerial := "12345678" // can be empty string if you only use one Yubikey device
	oathAccounts, err := ykmangoath.New(ctx, deviceSerial)
	// handler err

	// after this you can perform various operations on oathAccounts...
}

Once initialized, you may perform operations on oathAccounts such as List or Code methods. See Managing Password if you wish to support password protected Yubikey OATH applications (you really should).


Check if Device Available

To verify if the configured Yubikey device is connected & available:

IsAvailable := oathAccounts.IsAvailable()
fmt.Println(IsAvailable)

Example Go output:

true

List Accounts

List OATH accounts configured in the Yubikey device:

accounts, err := oathAccounts.List()
if err != nil {
  log.Fatal(err)
}

fmt.Println(accounts)

The above is the same as running the following in your terminal:

ykman --device 12345678 oath accounts list

Example Go output:

[
  "Amazon Web Services:[email protected]",
]

Request Code

Requests a Time-based One-Time Password (TOTP) 6-digit code for given account (such as ":") from Yubikey OATH application.

account := "<issuer>:<name>"
code, err := oathAccounts.Code(account)
if err != nil {
  log.Fatal(err)
}

fmt.Println(code)

The above is the same as running the following in your terminal:

ykman --device 12345678 oath accounts code --single '<issuer>:<name>'

Example Go output:

"123456"

Managing Password

An end-user with Yubikey device may wish to password protect the Yubikey's OATH application. Generally speaking this is a good idea as it adds some protection from theft: A bad actor with someone else's Yubikey device can't actually use the device to generate TOTP MFA codes unless they somehow also know the device password.

The password protection for the Yubikey device's OATH application can be set either via the Yubico Authenticator GUI application or via command-line with ykman by running:

ykman oath access change

But, if the device is configured with a password protected OATH application, it means that there needs to be a way to provide the password for ykmangoath: Luckily this is one of the benefits of this specific library as it supports just that by either:

There's also functionality to retrieve the prompted password given by end-user, so you may implement caching mechanisms to provide a smoother user experience where the end-user doesn't have to type in the password for every Yubikey operation; Remember there are of course tradeoffs with security vs. user experience with caching the password!

Check if Password Protected

To check if the Yubikey OATH application is password protected you can use:

isProtected := oathAccounts.IsPasswordProtected()
fmt.Println(isProtected)

Example Go output:

true

Direct Assign

err := oathAccounts.SetPassword("p4ssword")
// handle err
account := "<issuer>:<name>"
code, err := oathAccounts.Code(account)
// handle err

The above is the same as running the following in your terminal:

ykman --device 12345678 oath accounts code --single '<issuer>:<name>' --password 'p4ssword'

Prompt Function

Instead of assigning the password directly ahead-of-time, you may provide a password prompt function that will be executed only if password is required. Often you'll use this to actually ask the password from end-user – either via terminal stdin or by showing a GUI dialog with tools such as ncruces/zenity.

It must return a password string (which can be empty) and an error (which of course could be nil on success). The password prompt function will also receive the context.Context given in ykmangoath.New initialization, therefore your password prompt function can be cancelled (for example due to timeout).

func myPasswordPrompt(ctx context.Context) (string, error) {
	password := "p4ssword" // in real life, you'd resolve this value by asking the end-user
	return password, nil
}

err := oathAccounts.SetPasswordPrompt(myPasswordPrompt)

Retrieve the prompted Password

func myPasswordPrompt(ctx context.Context) (string, error) {
	password := "p4ssword" // in real life, you'd resolve this value by asking the end-user
	return password, nil
}

err := oathAccounts.SetPasswordPrompt(myPasswordPrompt)
// handle err

code, err := oathAccounts.Code("<issuer>:<name>")
// handle err
// do something with code

password, err := oathAccounts.GetPassword()
// handle err
// do something with password (e.g. cache it somewhere):
myCacheSolution.Set(password) // ... just an example

This can be useful if you wish to cache the Yubikey OATH application password for short periods of time in your own application, so that the user doesn't have to type in the password everytime (remember: the physical touch of the Yubikey device should be the actual second factor). How you cache it (hopefully somewhat securely) is up to you.


Design

This tool is designed only for retrieval of specific information from a Yubikey device:

  • List of configured OATH accounts
  • Time-based One-Time Password (TOTP) code for given OATH account
  • ... with a support for password protected Yubikey OATH applications

By design, this tool does NOT support:

  • Setting or changing the Yubikey OATH application password: Configuring the password should be an explicit end-user operation (which they can do via Yubico Authenticator GUI or ykman CLI) – We do not want to enable situations where this library renders end-user's Yubikey OATH accounts useless by setting a password unknown to the end-user.
  • Removing or renaming OATH accounts from the Yubikey device: This is another area where – either by accident or on purpose – one could harm the end-user.

If you need some of the above-mentioned unsupported features, feel free to implement them yourself – for example by forking this library but we will never accept a pull request back into this project which implements those features.

This tool may implement following features in the future:

  • #1 Adding new OATH acccounts
  • #2 Password protecting a Yubikey device's OATH application given that:
    1. there are no OATH accounts configured (i.e. the device OATH application is empty/unused)
    2. the device OATH application does not yet have a password protection enabled

But this is not a promise to implement them. If you feel like it, you can create a Pull Request!

You might also like...
Codes from "Esquenta Full-Cycle" Golang live

Golang Full Cycle livestream This was implemented in last week live from "Esquenta Full-Cycle" who is a pre-event warm up for the real Full-Cycle Imer

Another casbin implementation in golang

casbin Another casbin implementation in golang. Diffrent with the official casbin implementation Use google Common Expression Language as the matcher

Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to create powerful modern API and web authentication.

❗ Cache package has been moved to libcache repository Go-Guardian Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to

Go login handlers for authentication providers (OAuth1, OAuth2)
Go login handlers for authentication providers (OAuth1, OAuth2)

gologin Package gologin provides chainable login http.Handler's for Google, Github, Twitter, Facebook, Bitbucket, Tumblr, or any OAuth1 or OAuth2 auth

Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applications.

Goth: Multi-Provider Authentication for Go Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applic

HTTP Authentication middlewares

goji/httpauth httpauth currently provides HTTP Basic Authentication middleware for Go. It is compatible with Go's own net/http, goji, Gin & anything t

[DEPRECATED] Go package authcookie implements creation and verification of signed authentication cookies.

Package authcookie import "github.com/dchest/authcookie" Package authcookie implements creation and verification of signed authentication cookies. Co

Basic and Digest HTTP Authentication for golang http

HTTP Authentication implementation in Go This is an implementation of HTTP Basic and HTTP Digest authentication in Go language. It is designed as a si

Go (lang) HTTP session authentication

Go Session Authentication See git tags/releases for information about potentially breaking change. This package uses the Gorilla web toolkit's session

Comments
  • Relaxed device serial validation

    Relaxed device serial validation

    Device serial number validation pattern is too strict for older devices. My Yubikey 4 supports TOTP however its SN has a 7 only digits. I suggest completely relaxing the rule as a simple fixup.

    I can imagine device SN validation could be removed altogether at the cost of a BC break (ErrDeviceSerial removal).

    opened by RadekDvorak 2
  • Add support for enabling Password Protection

    Add support for enabling Password Protection

    This is a placeholder issue for an optional feature that may be implemented in the future (if there's a need).

    This is NOT a promise to implement!

    But if you feel like it, here's some information on how to get started with the implementation. Feel free to create a pull request!

    See also design decisions.


    The implementation in oath.go could be a method on the OathAccounts struct and look something like this:

    func (oa *OathAccounts) Protect(password string) error {}`
    

    In the background it should:

    1. check if there is already a password protection enabled
    2. check there are no OATH accounts configured (i.e. the device OATH application is empty/unused)
    3. implement ykman oath access change command with its various options.
    enhancement good first issue 
    opened by aripalo 0
  • Support adding new OATH acccounts

    Support adding new OATH acccounts

    This is a placeholder issue for an optional feature that may be implemented in the future (if there's a need).

    This is NOT a promise to implement!

    But if you feel like it, here's some information on how to get started with the implementation. Feel free to create a pull request!

    See also design decisions.


    The implementation in oath.go could be a method on the OathAccounts struct and look something like this:

    func (oa *OathAccounts) Add(options AccountOptions) error {}
    

    In the background it should implement ykman oath accounts add command with its various options.

    enhancement good first issue 
    opened by aripalo 0
Releases(v0.4.0)
Owner
Ari Palo
Lead Technologist at Alma Media
Ari Palo
Herbert Fischer 198 Oct 8, 2022
Authelia: an open-source authentication and authorization server providing two-factor authentication

Authelia is an open-source authentication and authorization server providing two

Streato 0 Jan 5, 2022
2FA (Two-Factor Authentication) application for CLI terminal with support to import/export andOTP files.

zauth zauth is a 2FA (Two-Factor Authentication) application for terminal written in Go. Features Supports both TOTP and HOTP codes. Add new entries d

Rijul Gulati 73 Nov 27, 2022
The Single Sign-On Multi-Factor portal for web apps

Authelia is an open-source authentication and authorization server providing two-factor authentication and single sign-on (SSO) for your applications

Authelia 14.8k Nov 27, 2022
Time-Based One-Time Password (TOTP) and HMAC-Based One-Time Password (HOTP) library for Go.

otpgo HMAC-Based and Time-Based One-Time Password (HOTP and TOTP) library for Go. Implements RFC 4226 and RFC 6238. Contents Supported Operations Read

Jose Torres 45 Nov 26, 2022
✨ Pretty TOTP manager in your terminal

asunder Asunder, Sweet and Other Distress asunder is a small pretty command-line TOTP manager. Installation Binaries Download a binary from the releas

obscurity 3 Sep 15, 2022
Authentication Plugin for implementing Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0, SAML Authentication

Authentication Plugin for implementing Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0, SAML Authentication

Paul Greenberg 653 Dec 8, 2022
A simple passwordless authentication middleware that uses only email as the authentication provider

email auth A simple passwordless authentication middleware that uses only email as the authentication provider. Motivation I wanted to restrict access

Miroslav Šedivý 5 Jul 27, 2022
Authorization and authentication. Learning go by writing a simple authentication and authorization service.

Authorization and authentication. Learning go by writing a simple authentication and authorization service.

Dinesh Bhattarai 0 Aug 5, 2022
A tool to manage accounts and codes of Google Authenticator.

A tool to manage accounts and codes of Google Authenticator.

Jormin 5 Sep 10, 2021