Device token api endpoint (#1)

* Added /device/token handler with associated business logic and storage tests.

* Use crypto rand for user code

Signed-off-by: justin-slowik <justin.slowik@thermofisher.com>
This commit is contained in:
Justin Slowik
2020-01-27 10:35:37 -05:00
committed by justin-slowik
parent 6d343e059b
commit 0d1a0e4129
10 changed files with 163 additions and 49 deletions

View File

@@ -5,7 +5,7 @@ import (
"encoding/base32"
"errors"
"io"
mrand "math/rand"
"math/big"
"strings"
"time"
@@ -25,6 +25,9 @@ var (
// TODO(ericchiang): refactor ID creation onto the storage.
var encoding = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")
//Valid characters for user codes
const validUserCharacters = "BCDFGHJKLMNPQRSTVWXZ"
// NewDeviceCode returns a 32 char alphanumeric cryptographically secure string
func NewDeviceCode() string {
return newSecureID(32)
@@ -79,6 +82,7 @@ type Storage interface {
GetPassword(email string) (Password, error)
GetOfflineSessions(userID string, connID string) (OfflineSessions, error)
GetConnector(id string) (Connector, error)
GetDeviceToken(deviceCode string) (DeviceToken, error)
ListClients() ([]Client, error)
ListRefreshTokens() ([]RefreshToken, error)
@@ -357,18 +361,24 @@ type Keys struct {
NextRotation time.Time
}
func NewUserCode() string {
mrand.Seed(time.Now().UnixNano())
return randomString(4) + "-" + randomString(4)
// NewUserCode returns a randomized 8 character user code for the device flow.
// No vowels are included to prevent accidental generation of words
func NewUserCode() (string, error) {
code, err := randomString(8)
if err != nil {
return "", err
}
return code[:4] + "-" + code[4:], nil
}
func randomString(n int) string {
var letter = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letter[mrand.Intn(len(letter))]
func randomString(n int) (string, error) {
v := big.NewInt(int64(len(validUserCharacters)))
bytes := make([]byte, n)
for i := 0; i < n; i++ {
c, _ := rand.Int(rand.Reader, v)
bytes[i] = validUserCharacters[c.Int64()]
}
return string(b)
return string(bytes), nil
}
//DeviceRequest represents an OIDC device authorization request. It holds the state of a device request until the user