Cotter's Event Response includes the following information:
{"ID": 1361, // Event ID"CreatedAt": "2020-02-27T22:22:48.705212512Z","UpdatedAt": "2020-02-27T22:22:48.705212512Z","DeletedAt": null,"client_user_id": "1014", // your client's User ID"issuer": "afcabd98-745f-4b9e-98de-af968d9951d3", // your API Key"event": "<EVENT NAME>",// requested event (LOGIN, or TRANSACTION, etc)"ip": "192.168.232.2","location": "Unknown","timestamp": "1582842167","method": "TRUSTED_DEVICE", // auth method: TRUSTED_DEVICE (other choices are PIN / BIOMETRIC)"new": false, // Is this a new pending event. More explanation below about Non-Trusted Device"approved": true, // Is this event approved."signature": "oonMGCAxp3..." // Signature to make sure this event comes from Cotter's server}
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
The signature ensures that this token comes from Cotter's server:
Signature algorithm: ed25519
Cotter's Public Key: qqOaiQGjGsxBMgI5rdAasaACRiJthOqadmefjY5mS/c=
Signed Message:
{event.client_user_id}{event.issuer}{event.event}{event.timestamp}{event.method}{event.new}{event.approved}
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
// https://golang.org/pkg/crypto/ed25519/func Verify(publicKey string, signStr string, args ...string) (bool, error) {str := []byte(strings.Join(args, ""))fmt.Println(strings.Join(args, ""))pubKey, err := base64.StdEncoding.DecodeString(publicKey)if err != nil {return false, responses.NewError("Fail verifying signature decoding pubkey", err)}signature, err := base64.StdEncoding.DecodeString(signStr)if err != nil {return false, responses.NewError("Fail verifying signature decoding signature", err)}valid := ed25519.Verify(pubKey, str, signature)return valid, nil}// Usagevalid, 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))
# https://pynacl.readthedocs.io/en/stable/signing/import binasciiimport nacl.signingimport nacl.encodingpub64 = "qqOaiQGjGsxBMgI5rdAasaACRiJthOqadmefjY5mS/c="signatureB64 = event["signature"]message = event["client_user_id"] + event["issuer"] + event["event"] + event["timestamp"] + event["method"] + event["new"] + event["approved"]signb = binascii.a2b_base64(signatureB64)msgb = str.encode(message)verify_key = nacl.signing.VerifyKey(pub64, encoder=nacl.encoding.Base64Encoder)verify_key.verify(msgb, signb)
import { Buffer } from "buffer";import { sign } from "tweetnacl";validateCotterEvent(event) {var message = `${event.client_user_id}${event.issuer}${event.event}${event.timestamp}${event.method}${event.new}${event.approved}`;var pubKey = "qqOaiQGjGsxBMgI5rdAasaACRiJthOqadmefjY5mS/c=";var signature = event.signature;const messageUint8 = new Uint8Array(Buffer.from(message, "utf8"));const signatureUint8 = new Uint8Array(Buffer.from(signature, "base64"));const pubKeyUint8 = new Uint8Array(Buffer.from(pubKey, "base64"));return sign.detached.verify(messageUint8, signatureUint8, pubKeyUint8);}
Libraries for ed25519
algorithm are available in Javascript, Golang, Python and other languages.