[{"data":1,"prerenderedAt":268},["ShallowReactive",2],{"docs-nav-\u002Fdocs\u002Faccount-sharing\u002Fadvanced\u002Fwebhooks":3,"\u002Fdocs\u002Faccount-sharing\u002Fadvanced\u002Fwebhooks":92},[4],{"title":5,"path":6,"stem":7,"children":8,"page":46},"Account Sharing","\u002Fdocs\u002Faccount-sharing","1.docs\u002F2.account sharing",[9,13,17,21,47,72],{"title":10,"path":11,"stem":12},"How it works","\u002Fdocs\u002Faccount-sharing\u002Fhow-account-sharing-prevention-works","1.docs\u002F2.account sharing\u002F0.How account sharing prevention works",{"title":14,"path":15,"stem":16},"Quick start","\u002Fdocs\u002Faccount-sharing\u002Fquick-start","1.docs\u002F2.account sharing\u002F1.Quick start",{"title":18,"path":19,"stem":20},"Events & actions","\u002Fdocs\u002Faccount-sharing\u002Flisten-to-events","1.docs\u002F2.account sharing\u002F2.Listen to events",{"title":22,"path":23,"stem":24,"children":25,"page":46},"Concepts","\u002Fdocs\u002Faccount-sharing\u002Fconcepts","1.docs\u002F2.account sharing\u002F4.Concepts",[26,30,34,38,42],{"title":27,"path":28,"stem":29},"Challenges","\u002Fdocs\u002Faccount-sharing\u002Fconcepts\u002Fchallenges","1.docs\u002F2.account sharing\u002F4.Concepts\u002F0.challenges",{"title":31,"path":32,"stem":33},"Device limits","\u002Fdocs\u002Faccount-sharing\u002Fconcepts\u002Fconfigure-device-limits","1.docs\u002F2.account sharing\u002F4.Concepts\u002F1.configure-device-limits",{"title":35,"path":36,"stem":37},"Groups","\u002Fdocs\u002Faccount-sharing\u002Fconcepts\u002Fgroups","1.docs\u002F2.account sharing\u002F4.Concepts\u002F2.groups",{"title":39,"path":40,"stem":41},"Automatically detach devices","\u002Fdocs\u002Faccount-sharing\u002Fconcepts\u002Fautomatically-detaching-devices","1.docs\u002F2.account sharing\u002F4.Concepts\u002F3.automatically-detaching-devices",{"title":43,"path":44,"stem":45},"Excluded Devices","\u002Fdocs\u002Faccount-sharing\u002Fconcepts\u002Fexcluded-devices","1.docs\u002F2.account sharing\u002F4.Concepts\u002F4.excluded-devices",false,{"title":48,"path":49,"stem":50,"children":51,"page":46},"Advanced","\u002Fdocs\u002Faccount-sharing\u002Fadvanced","1.docs\u002F2.account sharing\u002F8.Advanced",[52,56,60,64,68],{"title":53,"path":54,"stem":55},"Signing the user out","\u002Fdocs\u002Faccount-sharing\u002Fadvanced\u002Fsigning-the-user-out","1.docs\u002F2.account sharing\u002F8.Advanced\u002F1.Signing the user out",{"title":57,"path":58,"stem":59},"Webhooks","\u002Fdocs\u002Faccount-sharing\u002Fadvanced\u002Fwebhooks","1.docs\u002F2.account sharing\u002F8.Advanced\u002F2.webhooks",{"title":61,"path":62,"stem":63},"Metadata","\u002Fdocs\u002Faccount-sharing\u002Fadvanced\u002Fusing-metadata","1.docs\u002F2.account sharing\u002F8.Advanced\u002F3.Using metadata",{"title":65,"path":66,"stem":67},"When to attach","\u002Fdocs\u002Faccount-sharing\u002Fadvanced\u002Fwhen-to-call-the-attach-function","1.docs\u002F2.account sharing\u002F8.Advanced\u002F5.When to call the attach function",{"title":69,"path":70,"stem":71},"Tracking conversions","\u002Fdocs\u002Faccount-sharing\u002Fadvanced\u002Ftracking-conversions","1.docs\u002F2.account sharing\u002F8.Advanced\u002F8.Tracking conversions",{"title":73,"path":74,"stem":75,"children":76,"page":46},"Signals","\u002Fdocs\u002Faccount-sharing\u002Fsignals","1.docs\u002F2.account sharing\u002F9.Signals",[77,81,85,89],{"title":78,"path":79,"stem":80},"High velocity","\u002Fdocs\u002Faccount-sharing\u002Fsignals\u002Fhigh-velocity","1.docs\u002F2.account sharing\u002F9.Signals\u002F1.High velocity",{"title":82,"path":83,"stem":84},"Impossible travel","\u002Fdocs\u002Faccount-sharing\u002Fsignals\u002Fimpossible-travel","1.docs\u002F2.account sharing\u002F9.Signals\u002F2.Impossible travel",{"title":86,"path":87,"stem":88},"Concurrency","\u002Fdocs\u002Faccount-sharing\u002Fsignals\u002Fconcurrency","1.docs\u002F2.account sharing\u002F9.Signals\u002F3.Concurrency",{"title":31,"path":90,"stem":91},"\u002Fdocs\u002Faccount-sharing\u002Fsignals\u002Fdevice-limits","1.docs\u002F2.account sharing\u002F9.Signals\u002F4.Device limits",{"id":93,"title":57,"body":94,"description":261,"extension":262,"meta":263,"navigation":264,"path":58,"seo":265,"sitemap":266,"stem":59,"__hash__":267},"docs\u002F1.docs\u002F2.account sharing\u002F8.Advanced\u002F2.webhooks.md",{"type":95,"value":96,"toc":252},"minimark",[97,102,111,114,119,122,145,150,153,163,175,179,195,214,220,226,232,239,242,246],[98,99,101],"h1",{"id":100},"handle-webhook-events","Handle webhook events",[103,104,105,106],"p",{},"Rupt sends a growing number of webhook events. These events include information about the devices, users and challenges. To view information about specific webhook responses, ",[107,108,110],"a",{"href":109},"\u002Fapi\u002Fintroduction","view the api documentation",[103,112,113],{},"The way to indicate that a webhook has been processed is by returning a 2xx (status code 200-299) response to the webhook message within a reasonable time-frame (15s). It's also important to disable CSRF protection for this endpoint if the framework you use enables them by default.",[115,116,118],"h2",{"id":117},"verify-webhooks","Verify webhooks",[103,120,121],{},"Each webhook call includes three headers with additional information that are used for verification:",[123,124,125,133,139],"ul",{},[126,127,128,132],"li",{},[129,130,131],"code",{},"Webhook-Id",": the unique message identifier for the webhook message. This identifier is unique across all messages, but will be the same when the same webhook is being resent (e.g. due to a previous failure).",[126,134,135,138],{},[129,136,137],{},"Webhook-Timestamp",": timestamp in seconds since epoch.",[126,140,141,144],{},[129,142,143],{},"Webhook-Signature",": the Base64 encoded list of signatures (space delimited).",[146,147,149],"h3",{"id":148},"constructing-the-signed-content","Constructing the signed content",[103,151,152],{},"The content to sign is composed by concatenating the id, timestamp and payload, separated by the full-stop character (.). In code, it will look something like:",[154,155,160],"pre",{"className":156,"code":158,"language":159},[157],"language-text","signed_content = \"${webhook_id}.${webhook_timestamp}.${body}\"\n","text",[129,161,158],{"__ignoreMap":162},"",[103,164,165,166,169,170,174],{},"Where ",[129,167,168],{},"body"," is the raw body of the request. The signature is sensitive to any changes, so even a small change in the body will cause the signature to be completely different. This means that you should ",[171,172,173],"em",{},"not"," change the body in any way before verifying.",[146,176,178],{"id":177},"determining-the-expected-signature","Determining the expected signature",[103,180,181,182,188,189,194],{},"Rupt uses an ",[107,183,187],{"href":184,"rel":185},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHash-based_message_authentication_code",[186],"nofollow","HMAC"," with ",[107,190,193],{"href":191,"rel":192},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSHA-2",[186],"SHA-256"," to sign its webhooks.",[103,196,197,198,201,202,205,206,209,210,213],{},"So to calculate the expected signature, you should HMAC the ",[129,199,200],{},"signed_content"," from above using the base64 portion of your signing secret (this is the part after the ",[129,203,204],{},"whsec_"," prefix) as the key.\nFor example, given the secret ",[129,207,208],{},"whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"," you will want to use ",[129,211,212],{},"MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw",".",[103,215,216,217,219],{},"This generated signature should match one of the ones sent in the ",[129,218,143],{}," header.",[103,221,222,223,225],{},"The ",[129,224,143],{}," header is composed of a list of space delimited signatures and their corresponding version identifiers. The signature list is most commonly of length one. Though there could be any number of signatures. For example:",[154,227,230],{"className":228,"code":229,"language":159},[157],"v1,g0hM9SsE+OTPJTGt\u002FtmIKtSyZlE3uFJELVlNIOLJ1OE= v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo= v2,MzJsNDk4MzI0K2VvdSMjMTEjQEBAQDEyMzMzMzEyMwo=\n",[129,231,229],{"__ignoreMap":162},[103,233,234,235,238],{},"Make sure to remove the version prefix and delimiter (e.g. ",[129,236,237],{},"v1,",") before verifying the signature.",[103,240,241],{},"Please note that to compare the signatures it's recommended to use a constant-time string comparison method in order to prevent timing attacks.",[146,243,245],{"id":244},"verify-timestamp","Verify timestamp",[103,247,248,249,251],{},"As mentioned above, Rupt also sends the timestamp of the attempt in the ",[129,250,137],{}," header. You should compare this timestamp against your system timestamp and make sure it's within your tolerance in order to prevent timestamp attacks.",{"title":162,"searchDepth":253,"depth":253,"links":254},2,[255],{"id":117,"depth":253,"text":118,"children":256},[257,259,260],{"id":148,"depth":258,"text":149},3,{"id":177,"depth":258,"text":178},{"id":244,"depth":258,"text":245},"Receiving and handling webhook events from Rupt.","md",{},true,{"title":57,"description":261},{"loc":58},"ZNzy29fxJ_9YkdXcebKszmJy5Hrz7_FUPnHFP0yUBO0",1776564888376]