[{"data":1,"prerenderedAt":617},["ShallowReactive",2],{"\u002Fblog\u002Fcustom-challenges":3},{"id":4,"title":5,"accomplishment1":6,"accomplishment2":6,"accomplishment3":6,"author":7,"body":8,"category":605,"createdAt":606,"description":607,"executiveSummary":6,"extension":608,"founded":6,"headquarters":6,"img":609,"meta":610,"navigation":93,"og-img":609,"path":611,"seo":612,"sitemap":613,"size":6,"stem":614,"tags":615,"updatedAt":6,"website":6,"__hash__":616},"blog\u002F4.blog\u002F45.Custom challenges.md","Custom challenges for limiting device access",null,"Marc Whitbread",{"type":9,"value":10,"toc":593},"minimark",[11,15,25,30,38,43,55,248,252,260,302,306,314,348,374,383,387,405,478,482,493,539,548,552,560,582,586,589],[12,13,5],"h1",{"id":14},"custom-challenges-for-limiting-device-access",[16,17,18,19,24],"p",{},"For some use cases, you may want to create a completely custom challenge for a more seamless experience in your application. For these integrations, you can use the ",[20,21,23],"a",{"href":22},"\u002Fapi\u002Fchallenges","challenge public apis"," from your back-end to move your users through the challenge process.",[26,27,29],"h2",{"id":28},"the-rupt-client-sdk","The Rupt client SDK",[16,31,32,33,37],{},"The Rupt client SDK provides a way to attach a device to a user account and listen for incoming challenges. This enables you to create a custom challenge experience for your users. For a detailed explanation of the client SDK setup, see the ",[20,34,36],{"href":35},"\u002Fdocs\u002Fquick-start","Rupt documentation"," related to your platform.",[39,40,42],"h3",{"id":41},"attach-a-device-using-callbacks","Attach a device using callbacks",[16,44,45,46,50,51,54],{},"To listen for incoming challenges, you can use the ",[47,48,49],"code",{},"on_challenge"," callback. This callback will be called when a challenge is required for the current user. You can then use your custom challenge experience to resolve the challenge. To disable the default challenge experience, return ",[47,52,53],{},"false"," from the callback.",[56,57,62],"pre",{"className":58,"code":59,"language":60,"meta":61,"style":61},"language-js shiki shiki-themes one-dark-pro monokai","import Rupt from \"rupt\";\n\nconst { device_id } = await Rupt.attach({\n  client_id: \"CLIENT_ID\",\n  account: \"ACCOUNT_ID\",\n  on_challenge: ({ challenge }) => {\n    \u002F\u002F Your custom challenge experience\n    return false;\n  },\n  on_current_device_logout: ({ challenge }) => {\n    \u002F\u002F Your custom logout experience\n    return false;\n  },\n});\n","js","",[47,63,64,88,95,131,146,159,181,188,200,206,222,228,237,242],{"__ignoreMap":61},[65,66,69,73,77,80,84],"span",{"class":67,"line":68},"line",1,[65,70,72],{"class":71},"sqnWQ","import",[65,74,76],{"class":75},"sskXI"," Rupt",[65,78,79],{"class":71}," from",[65,81,83],{"class":82},"sgpKs"," \"rupt\"",[65,85,87],{"class":86},"sFXR2",";\n",[65,89,91],{"class":67,"line":90},2,[65,92,94],{"emptyLinePlaceholder":93},true,"\n",[65,96,98,102,105,109,112,116,119,121,124,128],{"class":67,"line":97},3,[65,99,101],{"class":100},"sfrnW","const",[65,103,104],{"class":86}," { ",[65,106,108],{"class":107},"syDQ0","device_id",[65,110,111],{"class":86}," } ",[65,113,115],{"class":114},"sBBc0","=",[65,117,118],{"class":71}," await",[65,120,76],{"class":107},[65,122,123],{"class":86},".",[65,125,127],{"class":126},"sUpKv","attach",[65,129,130],{"class":86},"({\n",[65,132,134,137,140,143],{"class":67,"line":133},4,[65,135,136],{"class":75},"  client_id",[65,138,139],{"class":86},": ",[65,141,142],{"class":82},"\"CLIENT_ID\"",[65,144,145],{"class":86},",\n",[65,147,149,152,154,157],{"class":67,"line":148},5,[65,150,151],{"class":75},"  account",[65,153,139],{"class":86},[65,155,156],{"class":82},"\"ACCOUNT_ID\"",[65,158,145],{"class":86},[65,160,162,165,168,172,175,178],{"class":67,"line":161},6,[65,163,164],{"class":126},"  on_challenge",[65,166,167],{"class":86},": ({ ",[65,169,171],{"class":170},"sLbzO","challenge",[65,173,174],{"class":86}," }) ",[65,176,177],{"class":100},"=>",[65,179,180],{"class":86}," {\n",[65,182,184],{"class":67,"line":183},7,[65,185,187],{"class":186},"sDDnO","    \u002F\u002F Your custom challenge experience\n",[65,189,191,194,198],{"class":67,"line":190},8,[65,192,193],{"class":71},"    return",[65,195,197],{"class":196},"sRaZW"," false",[65,199,87],{"class":86},[65,201,203],{"class":67,"line":202},9,[65,204,205],{"class":86},"  },\n",[65,207,209,212,214,216,218,220],{"class":67,"line":208},10,[65,210,211],{"class":126},"  on_current_device_logout",[65,213,167],{"class":86},[65,215,171],{"class":170},[65,217,174],{"class":86},[65,219,177],{"class":100},[65,221,180],{"class":86},[65,223,225],{"class":67,"line":224},11,[65,226,227],{"class":186},"    \u002F\u002F Your custom logout experience\n",[65,229,231,233,235],{"class":67,"line":230},12,[65,232,193],{"class":71},[65,234,197],{"class":196},[65,236,87],{"class":86},[65,238,240],{"class":67,"line":239},13,[65,241,205],{"class":86},[65,243,245],{"class":67,"line":244},14,[65,246,247],{"class":86},"});\n",[26,249,251],{"id":250},"the-rupt-core-sdk","The Rupt core SDK",[16,253,254,255,259],{},"On your server, you can use the Rupt core SDK to convenienty interact with the Rupt API. This SDK provides a way to retrieve challenges\u002Fdevices, kick devices, and complete challenges. See the ",[20,256,258],{"href":257},"\u002Fapi\u002Fintroduction","Rupt API documentation"," for specific intallation, setup and usage information.",[56,261,263],{"className":58,"code":262,"language":60,"meta":61,"style":61},"import RuptCore from \"@ruptjs\u002Fcore\";\nconst Rupt = new RuptCore(SECRET_KEY);\n",[47,264,265,279],{"__ignoreMap":61},[65,266,267,269,272,274,277],{"class":67,"line":68},[65,268,72],{"class":71},[65,270,271],{"class":75}," RuptCore",[65,273,79],{"class":71},[65,275,276],{"class":82}," \"@ruptjs\u002Fcore\"",[65,278,87],{"class":86},[65,280,281,283,285,288,291,293,296,299],{"class":67,"line":90},[65,282,101],{"class":100},[65,284,76],{"class":107},[65,286,287],{"class":114}," =",[65,289,290],{"class":71}," new",[65,292,271],{"class":126},[65,294,295],{"class":86},"(",[65,297,298],{"class":107},"SECRET_KEY",[65,300,301],{"class":86},");\n",[39,303,305],{"id":304},"retrieve-the-challenge-and-examine-the-limit-state","Retrieve the challenge and examine the limit state",[16,307,308,309,313],{},"To retrieve the challenge, you can use the ",[20,310,312],{"href":311},"\u002Fapi\u002Fchallenges\u002Fretrieve-a-challenge","get challenge"," endpoint. This endpoint will return the challenge details, including the current limit state. The limit state will indicate if and how many devices are required to resolve the challenge.",[56,315,317],{"className":58,"code":316,"language":60,"meta":61,"style":61},"const { limit } = await Rupt.getChallenge(CHALLENGE_ID);\n",[47,318,319],{"__ignoreMap":61},[65,320,321,323,325,328,330,332,334,336,338,341,343,346],{"class":67,"line":68},[65,322,101],{"class":100},[65,324,104],{"class":86},[65,326,327],{"class":107},"limit",[65,329,111],{"class":86},[65,331,115],{"class":114},[65,333,118],{"class":71},[65,335,76],{"class":107},[65,337,123],{"class":86},[65,339,340],{"class":126},"getChallenge",[65,342,295],{"class":86},[65,344,345],{"class":107},"CHALLENGE_ID",[65,347,301],{"class":86},[16,349,350,351,354,355,358,359,362,363,362,366,369,370,373],{},"When ",[47,352,353],{},"limit.is_exceeded"," is ",[47,356,357],{},"true",", the challenge is in a state where the device limit has been exceeded. You can then use the values of ",[47,360,361],{},"limit.overall",", ",[47,364,365],{},"limit.mobile",[47,367,368],{},"limit.tablet",", and ",[47,371,372],{},"limit.desktop"," to determine which types and how many of each device are required to be kicked to resolve the challenge.",[16,375,376,377,354,379,382],{},"For example, if ",[47,378,361],{},[47,380,381],{},"2",", you will need to kick any two devices to resolve the challenge.",[39,384,386],{"id":385},"retrieve-the-list-of-challenge-devices","Retrieve the list of challenge devices",[16,388,389,390,394,395,398,399,401,402,404],{},"Once the challenge is retrieved, you can use the ",[20,391,393],{"href":392},"\u002Fapi\u002Fchallenges\u002Fretrieve-challenge-devices","get challenge devices"," endpoint to retrieve the list of devices that are currently associated with the challenge. You can use the device ",[47,396,397],{},"type"," to determine which devices are required to be kicked to resolve the challenge. In addition, The ",[47,400,108],{}," returned from the ",[47,403,127],{}," method on the client can be used to identify which device represents a specific user in the challenge device list.",[56,406,408],{"className":58,"code":407,"language":60,"meta":61,"style":61},"const devices = await Rupt.getChallengeDevices(CHALLENGE_ID);\nconst currentUserDevice = devices.find(\n  ({ _id }) => _id === device_id_from_attach\n);\n",[47,409,410,434,453,474],{"__ignoreMap":61},[65,411,412,414,417,419,421,423,425,428,430,432],{"class":67,"line":68},[65,413,101],{"class":100},[65,415,416],{"class":107}," devices",[65,418,287],{"class":114},[65,420,118],{"class":71},[65,422,76],{"class":107},[65,424,123],{"class":86},[65,426,427],{"class":126},"getChallengeDevices",[65,429,295],{"class":86},[65,431,345],{"class":107},[65,433,301],{"class":86},[65,435,436,438,441,443,445,447,450],{"class":67,"line":90},[65,437,101],{"class":100},[65,439,440],{"class":107}," currentUserDevice",[65,442,287],{"class":114},[65,444,416],{"class":107},[65,446,123],{"class":86},[65,448,449],{"class":126},"find",[65,451,452],{"class":86},"(\n",[65,454,455,458,461,463,465,468,471],{"class":67,"line":97},[65,456,457],{"class":86},"  ({ ",[65,459,460],{"class":170},"_id",[65,462,174],{"class":86},[65,464,177],{"class":100},[65,466,467],{"class":75}," _id",[65,469,470],{"class":114}," ===",[65,472,473],{"class":75}," device_id_from_attach\n",[65,475,476],{"class":67,"line":133},[65,477,301],{"class":86},[39,479,481],{"id":480},"kick-devices-to-resolve-the-challenge","Kick devices to resolve the challenge",[16,483,484,485,489,490,492],{},"To resolve the challenge, you can use the ",[20,486,488],{"href":487},"\u002Fapi\u002Fchallenges\u002Fkick-a-challenge-device","kick challenge device"," endpoint to kick the required devices. You can use the ",[47,491,108],{}," to kick the device that represents the current user or any other device that is required to resolve the challenge specified in by the limit state.",[56,494,496],{"className":58,"code":495,"language":60,"meta":61,"style":61},"await Rupt.kickChallengeDevice({\n  challenge: CHALLENGE_ID,\n  device: DEVICE_ID,\n});\n",[47,497,498,512,523,535],{"__ignoreMap":61},[65,499,500,503,505,507,510],{"class":67,"line":68},[65,501,502],{"class":71},"await",[65,504,76],{"class":107},[65,506,123],{"class":86},[65,508,509],{"class":126},"kickChallengeDevice",[65,511,130],{"class":86},[65,513,514,517,519,521],{"class":67,"line":90},[65,515,516],{"class":75},"  challenge",[65,518,139],{"class":86},[65,520,345],{"class":107},[65,522,145],{"class":86},[65,524,525,528,530,533],{"class":67,"line":97},[65,526,527],{"class":75},"  device",[65,529,139],{"class":86},[65,531,532],{"class":107},"DEVICE_ID",[65,534,145],{"class":86},[65,536,537],{"class":67,"line":133},[65,538,247],{"class":86},[16,540,541,542,544,545,547],{},"It may be helpful to retrieve the challenge limit state again after kicking a device to ensure that the challenge has been resolved and the ",[47,543,353],{}," value is ",[47,546,53],{}," before proceeding.",[39,549,551],{"id":550},"complete-the-challenge","Complete the challenge",[16,553,554,555,559],{},"Once the challenge has been resolved, you can use the ",[20,556,558],{"href":557},"\u002Fapi\u002Fchallenges\u002Fcomplete-a-challenge","complete challenge"," endpoint to fully complete the challenge and allow the user to continue using your application.",[56,561,563],{"className":58,"code":562,"language":60,"meta":61,"style":61},"await Rupt.completeChallenge(CHALLENGE_ID);\n",[47,564,565],{"__ignoreMap":61},[65,566,567,569,571,573,576,578,580],{"class":67,"line":68},[65,568,502],{"class":71},[65,570,76],{"class":107},[65,572,123],{"class":86},[65,574,575],{"class":126},"completeChallenge",[65,577,295],{"class":86},[65,579,345],{"class":107},[65,581,301],{"class":86},[26,583,585],{"id":584},"conclusion","Conclusion",[16,587,588],{},"By using the Rupt client SDK and the Rupt core SDK, you can create a custom challenge experience for your users. This enables you to create a more seamless experience in your application and provide a more tailored solution for your specific use case.",[590,591,592],"style",{},"html pre.shiki code .sqnWQ, html code.shiki .sqnWQ{--shiki-default:#C678DD;--shiki-dark:#F92672}html pre.shiki code .sskXI, html code.shiki .sskXI{--shiki-default:#E06C75;--shiki-dark:#F8F8F2}html pre.shiki code .sgpKs, html code.shiki .sgpKs{--shiki-default:#98C379;--shiki-dark:#E6DB74}html pre.shiki code .sFXR2, html code.shiki .sFXR2{--shiki-default:#ABB2BF;--shiki-dark:#F8F8F2}html pre.shiki code .sfrnW, html code.shiki .sfrnW{--shiki-default:#C678DD;--shiki-default-font-style:inherit;--shiki-dark:#66D9EF;--shiki-dark-font-style:italic}html pre.shiki code .syDQ0, html code.shiki .syDQ0{--shiki-default:#E5C07B;--shiki-dark:#F8F8F2}html pre.shiki code .sBBc0, html code.shiki .sBBc0{--shiki-default:#56B6C2;--shiki-dark:#F92672}html pre.shiki code .sUpKv, html code.shiki .sUpKv{--shiki-default:#61AFEF;--shiki-dark:#A6E22E}html pre.shiki code .sLbzO, html code.shiki .sLbzO{--shiki-default:#E06C75;--shiki-default-font-style:italic;--shiki-dark:#FD971F;--shiki-dark-font-style:italic}html pre.shiki code .sDDnO, html code.shiki .sDDnO{--shiki-default:#7F848E;--shiki-default-font-style:italic;--shiki-dark:#88846F;--shiki-dark-font-style:inherit}html pre.shiki code .sRaZW, html code.shiki .sRaZW{--shiki-default:#D19A66;--shiki-dark:#AE81FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":61,"searchDepth":90,"depth":90,"links":594},[595,598,604],{"id":28,"depth":90,"text":29,"children":596},[597],{"id":41,"depth":97,"text":42},{"id":250,"depth":90,"text":251,"children":599},[600,601,602,603],{"id":304,"depth":97,"text":305},{"id":385,"depth":97,"text":386},{"id":480,"depth":97,"text":481},{"id":550,"depth":97,"text":551},{"id":584,"depth":90,"text":585},"Integrations","2024-09-17","You may want to create a completely custom challenge for a more seamless experience in your application. For these integrations, you can use the challenge public apis from your back-end to move your users through the challenge process.","md","\u002Fimg\u002Fblog\u002Fblog_custom_challenge.png",{},"\u002Fblog\u002Fcustom-challenges",{"title":5,"description":607},{"loc":611},"4.blog\u002F45.Custom challenges","authentication challenges, custom implementation, security features, user verification, account protection","WgmDnTS5EGM_dZ95Hcj1RSLC_p6WrEVnarbsWxT_qDs",1776449529155]