Validating Cotter's Event Response
Cotter's Event Response includes the following information:
1
{
2
"ID": 1361, // Event ID
3
"CreatedAt": "2020-02-27T22:22:48.705212512Z",
4
"UpdatedAt": "2020-02-27T22:22:48.705212512Z",
5
"DeletedAt": null,
6
"client_user_id": "1014", // your client's User ID
7
"issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key
8
"event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)
9
"ip": "192.168.232.2",
10
"location": "Unknown",
11
"timestamp": "1582842167",
12
"method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)
13
"new": false, // Is this a new pending event. More explanation below about Non-Trusted Device
14
"approved": true, // Is this event approved.
15
"signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server
16
}
Copied!
First, make sure you check the following:
  • Check if the client_user_id is correct
  • Check if the timestamp is fairly recent
  • Check if the event matches the event name that you expected
  • Check that the issuer is the same as your API_KEY_ID
  • Check that approved is true

Verifying the Signature

The signature ensures that this token comes from Cotter's server:
  • Signature algorithm: ed25519
  • Cotter's Public Key: qqOaiQGjGsxBMgI5rdAasaACRiJthOqadmefjY5mS/c=
  • Signed Message:
1
{event.client_user_id}{event.issuer}{event.event}{event.timestamp}{event.method}{event.new}{event.approved}
Copied!
Note that there is no space and no {} in the message. It's all just 1 long string. ex. 152163b39fb6eb-a1ff-4f7b-a205-d80f3c664cfdEVENTNAME1584659078TRUSTED_DEVICEfalsetrue

Example

Go
Python
JavaScript
1
// https://golang.org/pkg/crypto/ed25519/
2
func Verify(publicKey string, signStr string, args ...string) (bool, error) {
3
str := []byte(strings.Join(args, ""))
4
fmt.Println(strings.Join(args, ""))
5
pubKey, err := base64.StdEncoding.DecodeString(publicKey)
6
if err != nil {
7
return false, responses.NewError("Fail verifying signature decoding pubkey", err)
8
}
9
signature, err := base64.StdEncoding.DecodeString(signStr)
10
if err != nil {
11
return false, responses.NewError("Fail verifying signature decoding signature", err)
12
}
13
valid := ed25519.Verify(pubKey, str, signature)
14
return valid, nil
15
}
16
​
17
// Usage
18
valid, err := Verify(CotterPublicKey, event.Signature, event.ClientUserID, event.Issuer.String(), event.Event, event.Timestamp, string(event.Method), strconv.FormatBool(event.New), strconv.FormatBool(event.Approved))
Copied!
1
# https://pynacl.readthedocs.io/en/stable/signing/
2
import binascii
3
import nacl.signing
4
import nacl.encoding
5
​
6
pub64 = "qqOaiQGjGsxBMgI5rdAasaACRiJthOqadmefjY5mS/c="
7
signatureB64 = event["signature"]
8
message = event["client_user_id"] + event["issuer"] + event["event"] + event["timestamp"] + event["method"] + event["new"] + event["approved"]
9
​
10
signb = binascii.a2b_base64(signatureB64)
11
msgb = str.encode(message)
12
​
13
verify_key = nacl.signing.VerifyKey(pub64, encoder=nacl.encoding.Base64Encoder)
14
verify_key.verify(msgb, signb)
Copied!
1
import { Buffer } from "buffer";
2
import { sign } from "tweetnacl";
3
​
4
validateCotterEvent(event) {
5
var message = `${event.client_user_id}${event.issuer}${event.event}${event.timestamp}${event.method}${event.new}${event.approved}`;
6
var pubKey = "qqOaiQGjGsxBMgI5rdAasaACRiJthOqadmefjY5mS/c=";
7
var signature = event.signature;
8
9
const messageUint8 = new Uint8Array(Buffer.from(message, "utf8"));
10
const signatureUint8 = new Uint8Array(Buffer.from(signature, "base64"));
11
const pubKeyUint8 = new Uint8Array(Buffer.from(pubKey, "base64"));
12
13
return sign.detached.verify(messageUint8, signatureUint8, pubKeyUint8);
14
}
Copied!
Libraries for ed25519 algorithm are available in Javascript, Golang, Python and other languages. ​
Last modified 1yr ago
Copy link