Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Commit

Permalink
Emit ID for verification code (#215)
Browse files Browse the repository at this point in the history
* emit ID for verification code

* switch params

* fix unit

* unit test again

* base64 encode id to string

* url encoding

* comment

* hex encoding
  • Loading branch information
whaught authored Aug 10, 2020
1 parent 88a3982 commit 63bc844
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 19 deletions.
9 changes: 5 additions & 4 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
TestTypeConfirmed = "confirmed"
// TestTypeLikely is the string that represents a clinical diagnosis.
TestTypeLikely = "likely"
// TestTypeNegative is the string that represents a netgative test.
// TestTypeNegative is the string that represents a negative test.
TestTypeNegative = "negative"
)

Expand Down Expand Up @@ -63,13 +63,14 @@ type IssueCodeRequest struct {

// IssueCodeResponse defines the response type for IssueCodeRequest.
type IssueCodeResponse struct {
ID string `json:"id"` // Handle which allows the issuer to track status of the issued verification code.
VerificationCode string `json:"code"`
ExpiresAt string `json:"expiresAt"` // RFC1123 string formatted timestamp, in UTC.
ExpiresAtTimestamp int64 `json:"expiresAtTimestamp"` // Unix, seconds since the epoch. Still UTC.
Error string `json:"error"`
}

// VerifyCodeRequest is the request structure for exchanging a shor term Verification Code
// VerifyCodeRequest is the request structure for exchanging a short term Verification Code
// (OTP) for a long term token (a JWT) that can later be used to sign TEKs.
//
// Requires API key in a HTTP header, X-API-Key: APIKEY
Expand All @@ -79,7 +80,7 @@ type VerifyCodeRequest struct {

// VerifyCodeResponse either contains an error, or contains the test parameters
// (type and [optional] date) as well as the verification token. The verification token
// may be snet back on a valid VerificationCertificateRequest later.
// may be sent back on a valid VerificationCertificateRequest later.
type VerifyCodeResponse struct {
TestType string `json:"testtype"`
SymptomDate string `json:"symptomDate"` // ISO 8601 formatted date, YYYY-MM-DD
Expand All @@ -89,7 +90,7 @@ type VerifyCodeResponse struct {

// VerificationCertificateRequest is used to accept a long term token and
// an HMAC of the TEKs.
// The details of the HMAC calculation are avialble at:
// The details of the HMAC calculation are available at:
// https://github.com/google/exposure-notifications-server/blob/main/docs/design/verification_protocol.md
//
// Requires API key in a HTTP header, X-API-Key: APIKEY
Expand Down
10 changes: 9 additions & 1 deletion pkg/controller/issueapi/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package issueapi

import (
"encoding/binary"
"encoding/hex"
"fmt"
"net/http"
"strings"
Expand Down Expand Up @@ -121,7 +123,7 @@ func (c *Controller) HandleIssue() http.Handler {
RealmID: realm.ID,
}

code, err := codeRequest.Issue(ctx, c.config.GetCollisionRetryCount())
id, code, err := codeRequest.Issue(ctx, c.config.GetCollisionRetryCount())
if err != nil {
logger.Errorw("failed to issue code", "error", err)
c.h.RenderJSON(w, http.StatusInternalServerError, api.Errorf("failed to generate otp code, please try again"))
Expand All @@ -143,8 +145,14 @@ func (c *Controller) HandleIssue() http.Handler {
}
}

// Convert the uint to an encoded hex string for the response.
var intAsBytes []byte
binary.LittleEndian.PutUint64(intAsBytes, uint64(id))
idString := hex.EncodeToString(intAsBytes)

c.h.RenderJSON(w, http.StatusOK,
&api.IssueCodeResponse{
ID: idString,
VerificationCode: code,
ExpiresAt: expiryTime.Format(time.RFC1123),
ExpiresAtTimestamp: expiryTime.Unix(),
Expand Down
4 changes: 2 additions & 2 deletions pkg/database/vercode.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ var (
ErrTestTooOld = errors.New("test date is more than 14 day ago")
)

// VerificationCode represnts a verification code in the database.
// VerificationCode represents a verification code in the database.
type VerificationCode struct {
gorm.Model
RealmID uint // VerificationCodes belong to exactly one realm when issued.
Expand Down Expand Up @@ -74,7 +74,7 @@ func (v *VerificationCode) FormatSymptomDate() string {
return v.SymptomDate.Format("2006-01-02")
}

// IsExpired returns ture if a verification code has expired.
// IsExpired returns true if a verification code has expired.
func (v *VerificationCode) IsExpired() bool {
return !v.ExpiresAt.After(time.Now())
}
Expand Down
21 changes: 11 additions & 10 deletions pkg/otp/code.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,19 @@ type Request struct {
IssuingApp *database.AuthorizedApp
}

// Issue wiill generate a verification code and save it to the database, based
// on the paremters provited.
func (o *Request) Issue(ctx context.Context, retryCount uint) (string, error) {
// Issue will generate a verification code and save it to the database, based
// on the paremters provided.
func (o *Request) Issue(ctx context.Context, retryCount uint) (uint, string, error) {
logger := logging.FromContext(ctx)
var code string
var verificationCode database.VerificationCode
var err error
var i uint
for i = 0; i < retryCount; i++ {
code, err = GenerateCode(o.Length)
for i := uint(0); i < retryCount; i++ {
code, err := GenerateCode(o.Length)
if err != nil {
logger.Errorf("code generation error: %v", err)
continue
}
verificationCode := database.VerificationCode{
verificationCode = database.VerificationCode{
RealmID: o.RealmID,
Code: code,
TestType: strings.ToLower(o.TestType),
Expand All @@ -88,6 +87,8 @@ func (o *Request) Issue(ctx context.Context, retryCount uint) (string, error) {
break // successful save, nil error, break out.
}
}
return code, err

if err != nil {
return 0, "", err
}
return verificationCode.ID, verificationCode.Code, nil
}
4 changes: 2 additions & 2 deletions pkg/otp/code_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestIssue(t *testing.T) {
ExpiresAt: time.Now().Add(time.Hour),
TestType: "confirmed",
}
code, err := otp.Issue(ctx, 10)
_, code, err := otp.Issue(ctx, 10)
if err != nil {
t.Errorf("error generating code: %v", err)
}
Expand All @@ -72,7 +72,7 @@ func TestIssue(t *testing.T) {
if err != nil {
t.Errorf("didn't find previously saved code")
}
if verCode.Code != code {
if verCode != nil && verCode.Code != code {
t.Fatalf("loaded code doesn't match requested code")
}
}
Expand Down

0 comments on commit 63bc844

Please sign in to comment.