Device flow token code exchange (#2)

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

Perform user code exchange, flag the device code as complete.

Moved device handler code into its own file for cleanliness.  Cleanup

* Removed PKCE code

* Rate limiting for /device/token endpoint based on ietf standards

* Configurable Device expiry

Signed-off-by: justin-slowik <justin.slowik@thermofisher.com>
This commit is contained in:
Justin Slowik
2020-01-28 14:14:30 -05:00
committed by justin-slowik
parent 0d1a0e4129
commit 9bbdc721d5
20 changed files with 777 additions and 274 deletions

View File

@@ -888,12 +888,12 @@ func (c *conn) delete(table, field, id string) error {
func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error {
_, err := c.Exec(`
insert into device_request (
user_code, device_code, client_id, scopes, pkce_verifier, expiry
user_code, device_code, client_id, scopes, expiry
)
values (
$1, $2, $3, $4, $5, $6
$1, $2, $3, $4, $5
);`,
d.UserCode, d.DeviceCode, d.ClientID, encoder(d.Scopes), d.PkceVerifier, d.Expiry,
d.UserCode, d.DeviceCode, d.ClientID, encoder(d.Scopes), d.Expiry,
)
if err != nil {
if c.alreadyExistsCheck(err) {
@@ -907,12 +907,12 @@ func (c *conn) CreateDeviceRequest(d storage.DeviceRequest) error {
func (c *conn) CreateDeviceToken(t storage.DeviceToken) error {
_, err := c.Exec(`
insert into device_token (
device_code, status, token, expiry
device_code, status, token, expiry, last_request, poll_interval
)
values (
$1, $2, $3, $4
$1, $2, $3, $4, $5, $6
);`,
t.DeviceCode, t.Status, t.Token, t.Expiry,
t.DeviceCode, t.Status, t.Token, t.Expiry, t.LastRequestTime, t.PollIntervalSeconds,
)
if err != nil {
if c.alreadyExistsCheck(err) {
@@ -923,6 +923,28 @@ func (c *conn) CreateDeviceToken(t storage.DeviceToken) error {
return nil
}
func (c *conn) GetDeviceRequest(userCode string) (storage.DeviceRequest, error) {
return getDeviceRequest(c, userCode)
}
func getDeviceRequest(q querier, userCode string) (d storage.DeviceRequest, err error) {
err = q.QueryRow(`
select
device_code, client_id, scopes, expiry
from device_request where user_code = $1;
`, userCode).Scan(
&d.DeviceCode, &d.ClientID, decoder(&d.Scopes), &d.Expiry,
)
if err != nil {
if err == sql.ErrNoRows {
return d, storage.ErrNotFound
}
return d, fmt.Errorf("select device token: %v", err)
}
d.UserCode = userCode
return d, nil
}
func (c *conn) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) {
return getDeviceToken(c, deviceCode)
}
@@ -930,10 +952,10 @@ func (c *conn) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) {
func getDeviceToken(q querier, deviceCode string) (a storage.DeviceToken, err error) {
err = q.QueryRow(`
select
status, token, expiry
status, token, expiry, last_request, poll_interval
from device_token where device_code = $1;
`, deviceCode).Scan(
&a.Status, &a.Token, &a.Expiry,
&a.Status, &a.Token, &a.Expiry, &a.LastRequestTime, &a.PollIntervalSeconds,
)
if err != nil {
if err == sql.ErrNoRows {
@@ -944,3 +966,31 @@ func getDeviceToken(q querier, deviceCode string) (a storage.DeviceToken, err er
a.DeviceCode = deviceCode
return a, nil
}
func (c *conn) UpdateDeviceToken(deviceCode string, updater func(old storage.DeviceToken) (storage.DeviceToken, error)) error {
return c.ExecTx(func(tx *trans) error {
r, err := getDeviceToken(tx, deviceCode)
if err != nil {
return err
}
if r, err = updater(r); err != nil {
return err
}
_, err = tx.Exec(`
update device_token
set
status = $1,
token = $2,
last_request = $3,
poll_interval = $4
where
device_code = $5
`,
r.Status, r.Token, r.LastRequestTime, r.PollIntervalSeconds, r.DeviceCode,
)
if err != nil {
return fmt.Errorf("update device token: %v", err)
}
return nil
})
}

View File

@@ -236,7 +236,6 @@ var migrations = []migration{
device_code text not null,
client_id text not null,
scopes bytea not null, -- JSON array of strings
pkce_verifier text not null,
expiry timestamptz not null
);`,
`
@@ -244,7 +243,9 @@ var migrations = []migration{
device_code text not null primary key,
status text not null,
token text,
expiry timestamptz not null
expiry timestamptz not null,
last_request timestamptz not null,
poll_interval integer not null
);`,
},
},