[{"data":1,"prerenderedAt":1160},["ShallowReactive",2],{"docs:/docs/client-tokens":3},{"id":4,"title":5,"body":6,"description":1153,"extension":1154,"meta":1155,"navigation":313,"path":1156,"seo":1157,"stem":1158,"__hash__":1159},"docs/docs/client-tokens.md","Client Tokens",{"type":7,"value":8,"toc":1140},"minimark",[9,13,17,20,25,62,65,69,97,111,122,126,132,364,373,377,380,731,736,812,818,822,825,942,957,960,971,975,1002,1006,1009,1026,1030,1112,1116,1136],[10,11,5],"h1",{"id":12},"client-tokens",[14,15,16],"p",{},"Actor access tokens are long-lived credentials. They are the right tool for servers, agents, and devices you control, but they should never be shipped to a browser: anyone who opens devtools can read the token and reuse it until you revoke it.",[14,18,19],{},"Client tokens solve this. Your backend mints a short-lived JWT (signed with a project-level signing key) and hands it to the browser. The JWT points at an actor; all permissions still resolve from that actor on the NoLag side. If a client token leaks, it expires on its own within minutes.",[21,22,24],"h2",{"id":23},"how-it-works","How It Works",[26,27,28,42,53,59],"ol",{},[29,30,31,32,36,37,41],"li",{},"You create a ",[33,34,35],"strong",{},"signing key"," for your project (once) and store it on your backend as ",[38,39,40],"code",{},"NOLAG_SIGNING_KEY",".",[29,43,44,45,48,49,52],{},"You create an ",[33,46,47],{},"actor"," for each of your users and store the actor's public ",[38,50,51],{},"keyId"," against your user record. The actor's access token itself is not needed for the browser flow, so you can discard it.",[29,54,55,56,58],{},"When a user opens your app, your backend mints a JWT: signed with the signing key, naming the user's actor ",[38,57,51],{},", expiring in a few minutes.",[29,60,61],{},"The browser connects with that JWT. NoLag verifies the signature, resolves the actor, and applies exactly the same permissions the actor would have with its access token.",[14,63,64],{},"No NoLag API call is needed to mint a token. Signing happens entirely on your backend with a standard JWT library.",[21,66,68],{"id":67},"creating-a-signing-key","Creating a Signing Key",[26,70,71,81,84,94],{},[29,72,73,74],{},"Log in to the ",[75,76,80],"a",{"href":77,"rel":78},"https://portal.nolag.app",[79],"nofollow","NoLag Dashboard",[29,82,83],{},"Navigate to your project",[29,85,86,87,90,91],{},"Go to ",[33,88,89],{},"Settings"," then ",[33,92,93],{},"Signing Keys",[29,95,96],{},"Create a key and copy it (shown only once on creation)",[14,98,99,100,103,104,107,108,110],{},"The key has the format ",[38,101,102],{},"sk_live_\u003CkeyId>.\u003Csecret>",", the same shape as other NoLag credentials. The part before the dot is public and goes in the JWT header as ",[38,105,106],{},"kid",". The part after the dot is the HS256 signing secret. Store the whole string as ",[38,109,40],{}," on your backend and never expose it to a client.",[14,112,113,114,118,119,41],{},"Signing keys can also be managed with the ",[75,115,117],{"href":116},"/docs/api-reference","REST API"," at ",[38,120,121],{},"/v1/signing-keys",[21,123,125],{"id":124},"creating-an-actor-per-user","Creating an Actor per User",[14,127,128,129,131],{},"Create an actor when a user signs up (or lazily on first use) and save only the public ",[38,130,51],{}," on your user record:",[133,134,140],"pre",{"className":135,"code":136,"filename":137,"language":138,"meta":139,"style":139},"language-typescript shiki shiki-themes github-light github-dark","const response = await fetch('https://api.nolag.app/v1/actors', {\n  method: 'POST',\n  headers: {\n    Authorization: `Bearer ${process.env.NOLAG_API_KEY}`,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    name: `User ${user.id}`,\n    actorType: 'user',\n    metadata: { userId: user.id },\n  }),\n})\n\nconst actor = await response.json()\n// Save actor.keyId (e.g. \"at_live_abc123def456\") on your user record.\n// The accessToken is not needed for the browser flow; discard it.\nawait db.users.update(user.id, { nolagKeyId: actor.keyId })\n","Node.js","typescript","",[38,141,142,176,188,194,221,235,241,258,279,290,296,302,308,315,336,343,349],{"__ignoreMap":139},[143,144,147,151,155,158,161,165,169,173],"span",{"class":145,"line":146},"line",1,[143,148,150],{"class":149},"szBVR","const",[143,152,154],{"class":153},"sj4cs"," response",[143,156,157],{"class":149}," =",[143,159,160],{"class":149}," await",[143,162,164],{"class":163},"sScJk"," fetch",[143,166,168],{"class":167},"sVt8B","(",[143,170,172],{"class":171},"sZZnC","'https://api.nolag.app/v1/actors'",[143,174,175],{"class":167},", {\n",[143,177,179,182,185],{"class":145,"line":178},2,[143,180,181],{"class":167},"  method: ",[143,183,184],{"class":171},"'POST'",[143,186,187],{"class":167},",\n",[143,189,191],{"class":145,"line":190},3,[143,192,193],{"class":167},"  headers: {\n",[143,195,197,200,203,206,208,211,213,216,219],{"class":145,"line":196},4,[143,198,199],{"class":167},"    Authorization: ",[143,201,202],{"class":171},"`Bearer ${",[143,204,205],{"class":167},"process",[143,207,41],{"class":171},[143,209,210],{"class":167},"env",[143,212,41],{"class":171},[143,214,215],{"class":153},"NOLAG_API_KEY",[143,217,218],{"class":171},"}`",[143,220,187],{"class":167},[143,222,224,227,230,233],{"class":145,"line":223},5,[143,225,226],{"class":171},"    'Content-Type'",[143,228,229],{"class":167},": ",[143,231,232],{"class":171},"'application/json'",[143,234,187],{"class":167},[143,236,238],{"class":145,"line":237},6,[143,239,240],{"class":167},"  },\n",[143,242,244,247,250,252,255],{"class":145,"line":243},7,[143,245,246],{"class":167},"  body: ",[143,248,249],{"class":153},"JSON",[143,251,41],{"class":167},[143,253,254],{"class":163},"stringify",[143,256,257],{"class":167},"({\n",[143,259,261,264,267,270,272,275,277],{"class":145,"line":260},8,[143,262,263],{"class":167},"    name: ",[143,265,266],{"class":171},"`User ${",[143,268,269],{"class":167},"user",[143,271,41],{"class":171},[143,273,274],{"class":167},"id",[143,276,218],{"class":171},[143,278,187],{"class":167},[143,280,282,285,288],{"class":145,"line":281},9,[143,283,284],{"class":167},"    actorType: ",[143,286,287],{"class":171},"'user'",[143,289,187],{"class":167},[143,291,293],{"class":145,"line":292},10,[143,294,295],{"class":167},"    metadata: { userId: user.id },\n",[143,297,299],{"class":145,"line":298},11,[143,300,301],{"class":167},"  }),\n",[143,303,305],{"class":145,"line":304},12,[143,306,307],{"class":167},"})\n",[143,309,311],{"class":145,"line":310},13,[143,312,314],{"emptyLinePlaceholder":313},true,"\n",[143,316,318,320,323,325,327,330,333],{"class":145,"line":317},14,[143,319,150],{"class":149},[143,321,322],{"class":153}," actor",[143,324,157],{"class":149},[143,326,160],{"class":149},[143,328,329],{"class":167}," response.",[143,331,332],{"class":163},"json",[143,334,335],{"class":167},"()\n",[143,337,339],{"class":145,"line":338},15,[143,340,342],{"class":341},"sJ8bj","// Save actor.keyId (e.g. \"at_live_abc123def456\") on your user record.\n",[143,344,346],{"class":145,"line":345},16,[143,347,348],{"class":341},"// The accessToken is not needed for the browser flow; discard it.\n",[143,350,352,355,358,361],{"class":145,"line":351},17,[143,353,354],{"class":149},"await",[143,356,357],{"class":167}," db.users.",[143,359,360],{"class":163},"update",[143,362,363],{"class":167},"(user.id, { nolagKeyId: actor.keyId })\n",[14,365,366,367,369,370,372],{},"The ",[38,368,51],{}," is a public identifier, safe to store in your database without encryption. Actor management endpoints (get, update, delete) accept the ",[38,371,51],{}," in place of the actor UUID, so it is the only value you need to keep.",[21,374,376],{"id":375},"minting-a-client-token","Minting a Client Token",[14,378,379],{},"Mint a fresh token whenever your frontend asks for one. Use any standard JWT library:",[381,382,383,548,619],"code-tabs",{},[133,384,386],{"className":135,"code":385,"filename":137,"language":138,"meta":139,"style":139},"import jwt from 'jsonwebtoken'\n\nconst [kid, secret] = process.env.NOLAG_SIGNING_KEY.split('.')\n\napp.get('/api/nolag-token', requireLogin, (req, res) => {\n  const token = jwt.sign(\n    { sub: req.user.nolagKeyId },\n    secret,\n    { algorithm: 'HS256', keyid: kid, expiresIn: '15m' },\n  )\n  res.json({ token })\n})\n",[38,387,388,402,406,445,449,483,502,507,512,529,534,544],{"__ignoreMap":139},[143,389,390,393,396,399],{"class":145,"line":146},[143,391,392],{"class":149},"import",[143,394,395],{"class":167}," jwt ",[143,397,398],{"class":149},"from",[143,400,401],{"class":171}," 'jsonwebtoken'\n",[143,403,404],{"class":145,"line":178},[143,405,314],{"emptyLinePlaceholder":313},[143,407,408,410,413,415,418,421,424,427,430,432,434,437,439,442],{"class":145,"line":190},[143,409,150],{"class":149},[143,411,412],{"class":167}," [",[143,414,106],{"class":153},[143,416,417],{"class":167},", ",[143,419,420],{"class":153},"secret",[143,422,423],{"class":167},"] ",[143,425,426],{"class":149},"=",[143,428,429],{"class":167}," process.env.",[143,431,40],{"class":153},[143,433,41],{"class":167},[143,435,436],{"class":163},"split",[143,438,168],{"class":167},[143,440,441],{"class":171},"'.'",[143,443,444],{"class":167},")\n",[143,446,447],{"class":145,"line":196},[143,448,314],{"emptyLinePlaceholder":313},[143,450,451,454,457,459,462,465,469,471,474,477,480],{"class":145,"line":223},[143,452,453],{"class":167},"app.",[143,455,456],{"class":163},"get",[143,458,168],{"class":167},[143,460,461],{"class":171},"'/api/nolag-token'",[143,463,464],{"class":167},", requireLogin, (",[143,466,468],{"class":467},"s4XuR","req",[143,470,417],{"class":167},[143,472,473],{"class":467},"res",[143,475,476],{"class":167},") ",[143,478,479],{"class":149},"=>",[143,481,482],{"class":167}," {\n",[143,484,485,488,491,493,496,499],{"class":145,"line":237},[143,486,487],{"class":149},"  const",[143,489,490],{"class":153}," token",[143,492,157],{"class":149},[143,494,495],{"class":167}," jwt.",[143,497,498],{"class":163},"sign",[143,500,501],{"class":167},"(\n",[143,503,504],{"class":145,"line":243},[143,505,506],{"class":167},"    { sub: req.user.nolagKeyId },\n",[143,508,509],{"class":145,"line":260},[143,510,511],{"class":167},"    secret,\n",[143,513,514,517,520,523,526],{"class":145,"line":281},[143,515,516],{"class":167},"    { algorithm: ",[143,518,519],{"class":171},"'HS256'",[143,521,522],{"class":167},", keyid: kid, expiresIn: ",[143,524,525],{"class":171},"'15m'",[143,527,528],{"class":167}," },\n",[143,530,531],{"class":145,"line":292},[143,532,533],{"class":167},"  )\n",[143,535,536,539,541],{"class":145,"line":298},[143,537,538],{"class":167},"  res.",[143,540,332],{"class":163},[143,542,543],{"class":167},"({ token })\n",[143,545,546],{"class":145,"line":304},[143,547,307],{"class":167},[133,549,554],{"className":550,"code":551,"filename":552,"language":553,"meta":139,"style":139},"language-python shiki shiki-themes github-light github-dark","import os, time, jwt  # PyJWT\n\nkid, secret = os.environ[\"NOLAG_SIGNING_KEY\"].split(\".\")\n\n@app.get(\"/api/nolag-token\")\ndef nolag_token(user=Depends(require_login)):\n    token = jwt.encode(\n        {\"sub\": user.nolag_key_id, \"exp\": int(time.time()) + 900},\n        secret,\n        algorithm=\"HS256\",\n        headers={\"kid\": kid},\n    )\n    return {\"token\": token}\n","Python","python",[38,555,556,561,565,570,574,579,584,589,594,599,604,609,614],{"__ignoreMap":139},[143,557,558],{"class":145,"line":146},[143,559,560],{},"import os, time, jwt  # PyJWT\n",[143,562,563],{"class":145,"line":178},[143,564,314],{"emptyLinePlaceholder":313},[143,566,567],{"class":145,"line":190},[143,568,569],{},"kid, secret = os.environ[\"NOLAG_SIGNING_KEY\"].split(\".\")\n",[143,571,572],{"class":145,"line":196},[143,573,314],{"emptyLinePlaceholder":313},[143,575,576],{"class":145,"line":223},[143,577,578],{},"@app.get(\"/api/nolag-token\")\n",[143,580,581],{"class":145,"line":237},[143,582,583],{},"def nolag_token(user=Depends(require_login)):\n",[143,585,586],{"class":145,"line":243},[143,587,588],{},"    token = jwt.encode(\n",[143,590,591],{"class":145,"line":260},[143,592,593],{},"        {\"sub\": user.nolag_key_id, \"exp\": int(time.time()) + 900},\n",[143,595,596],{"class":145,"line":281},[143,597,598],{},"        secret,\n",[143,600,601],{"class":145,"line":292},[143,602,603],{},"        algorithm=\"HS256\",\n",[143,605,606],{"class":145,"line":298},[143,607,608],{},"        headers={\"kid\": kid},\n",[143,610,611],{"class":145,"line":304},[143,612,613],{},"    )\n",[143,615,616],{"class":145,"line":310},[143,617,618],{},"    return {\"token\": token}\n",[133,620,625],{"className":621,"code":622,"filename":623,"language":624,"meta":139,"style":139},"language-go shiki shiki-themes github-light github-dark","package main\n\nimport (\n    \"os\"\n    \"strings\"\n    \"time\"\n\n    \"github.com/golang-jwt/jwt/v5\"\n)\n\nfunc mintClientToken(actorKeyID string) (string, error) {\n    parts := strings.SplitN(os.Getenv(\"NOLAG_SIGNING_KEY\"), \".\", 2)\n    kid, secret := parts[0], parts[1]\n\n    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{\n        \"sub\": actorKeyID,\n        \"exp\": time.Now().Add(15 * time.Minute).Unix(),\n    })\n    token.Header[\"kid\"] = kid\n    return token.SignedString([]byte(secret))\n}\n","Go","go",[38,626,627,632,636,641,646,651,656,660,665,669,673,678,683,688,692,697,702,707,713,719,725],{"__ignoreMap":139},[143,628,629],{"class":145,"line":146},[143,630,631],{},"package main\n",[143,633,634],{"class":145,"line":178},[143,635,314],{"emptyLinePlaceholder":313},[143,637,638],{"class":145,"line":190},[143,639,640],{},"import (\n",[143,642,643],{"class":145,"line":196},[143,644,645],{},"    \"os\"\n",[143,647,648],{"class":145,"line":223},[143,649,650],{},"    \"strings\"\n",[143,652,653],{"class":145,"line":237},[143,654,655],{},"    \"time\"\n",[143,657,658],{"class":145,"line":243},[143,659,314],{"emptyLinePlaceholder":313},[143,661,662],{"class":145,"line":260},[143,663,664],{},"    \"github.com/golang-jwt/jwt/v5\"\n",[143,666,667],{"class":145,"line":281},[143,668,444],{},[143,670,671],{"class":145,"line":292},[143,672,314],{"emptyLinePlaceholder":313},[143,674,675],{"class":145,"line":298},[143,676,677],{},"func mintClientToken(actorKeyID string) (string, error) {\n",[143,679,680],{"class":145,"line":304},[143,681,682],{},"    parts := strings.SplitN(os.Getenv(\"NOLAG_SIGNING_KEY\"), \".\", 2)\n",[143,684,685],{"class":145,"line":310},[143,686,687],{},"    kid, secret := parts[0], parts[1]\n",[143,689,690],{"class":145,"line":317},[143,691,314],{"emptyLinePlaceholder":313},[143,693,694],{"class":145,"line":338},[143,695,696],{},"    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{\n",[143,698,699],{"class":145,"line":345},[143,700,701],{},"        \"sub\": actorKeyID,\n",[143,703,704],{"class":145,"line":351},[143,705,706],{},"        \"exp\": time.Now().Add(15 * time.Minute).Unix(),\n",[143,708,710],{"class":145,"line":709},18,[143,711,712],{},"    })\n",[143,714,716],{"class":145,"line":715},19,[143,717,718],{},"    token.Header[\"kid\"] = kid\n",[143,720,722],{"class":145,"line":721},20,[143,723,724],{},"    return token.SignedString([]byte(secret))\n",[143,726,728],{"class":145,"line":727},21,[143,729,730],{},"}\n",[732,733,735],"h3",{"id":734},"token-rules","Token Rules",[737,738,739,752],"table",{},[740,741,742],"thead",{},[743,744,745,749],"tr",{},[746,747,748],"th",{},"Field",[746,750,751],{},"Requirement",[753,754,755,771,784,802],"tbody",{},[743,756,757,764],{},[758,759,760,761],"td",{},"Header ",[38,762,763],{},"alg",[758,765,766,767,770],{},"Must be ",[38,768,769],{},"HS256",". Other algorithms are rejected.",[743,772,773,777],{},[758,774,760,775],{},[38,776,106],{},[758,778,779,780,783],{},"Your signing key's public id (",[38,781,782],{},"sk_live_...","). Required.",[743,785,786,792],{},[758,787,788,789],{},"Claim ",[38,790,791],{},"sub",[758,793,794,795,797,798,801],{},"The actor's public ",[38,796,51],{}," (",[38,799,800],{},"at_live_...","). Required. The actor must belong to the same project as the signing key.",[743,803,804,809],{},[758,805,788,806],{},[38,807,808],{},"exp",[758,810,811],{},"Required. At most 1 hour in the future. We recommend 15 minutes.",[14,813,814,815,817],{},"Clock skew of up to 60 seconds is tolerated on ",[38,816,808],{},". Any token that fails a check is rejected with a generic authentication error.",[21,819,821],{"id":820},"connecting-from-the-browser","Connecting from the Browser",[14,823,824],{},"Pass a token provider function to the JS SDK instead of a token string. The SDK calls it on every connect and reconnect, so each attempt uses a freshly minted token:",[133,826,829],{"className":135,"code":827,"filename":828,"language":138,"meta":139,"style":139},"import { NoLag } from '@nolag/js-sdk'\n\nconst client = NoLag(async () => {\n  const res = await fetch('/api/nolag-token')\n  const { token } = await res.json()\n  return token\n})\n\nawait client.connect()\n","Browser",[38,830,831,843,847,871,890,914,922,926,930],{"__ignoreMap":139},[143,832,833,835,838,840],{"class":145,"line":146},[143,834,392],{"class":149},[143,836,837],{"class":167}," { NoLag } ",[143,839,398],{"class":149},[143,841,842],{"class":171}," '@nolag/js-sdk'\n",[143,844,845],{"class":145,"line":178},[143,846,314],{"emptyLinePlaceholder":313},[143,848,849,851,854,856,859,861,864,867,869],{"class":145,"line":190},[143,850,150],{"class":149},[143,852,853],{"class":153}," client",[143,855,157],{"class":149},[143,857,858],{"class":163}," NoLag",[143,860,168],{"class":167},[143,862,863],{"class":149},"async",[143,865,866],{"class":167}," () ",[143,868,479],{"class":149},[143,870,482],{"class":167},[143,872,873,875,878,880,882,884,886,888],{"class":145,"line":196},[143,874,487],{"class":149},[143,876,877],{"class":153}," res",[143,879,157],{"class":149},[143,881,160],{"class":149},[143,883,164],{"class":163},[143,885,168],{"class":167},[143,887,461],{"class":171},[143,889,444],{"class":167},[143,891,892,894,897,900,903,905,907,910,912],{"class":145,"line":223},[143,893,487],{"class":149},[143,895,896],{"class":167}," { ",[143,898,899],{"class":153},"token",[143,901,902],{"class":167}," } ",[143,904,426],{"class":149},[143,906,160],{"class":149},[143,908,909],{"class":167}," res.",[143,911,332],{"class":163},[143,913,335],{"class":167},[143,915,916,919],{"class":145,"line":237},[143,917,918],{"class":149},"  return",[143,920,921],{"class":167}," token\n",[143,923,924],{"class":145,"line":243},[143,925,307],{"class":167},[143,927,928],{"class":145,"line":260},[143,929,314],{"emptyLinePlaceholder":313},[143,931,932,934,937,940],{"class":145,"line":281},[143,933,354],{"class":149},[143,935,936],{"class":167}," client.",[143,938,939],{"class":163},"connect",[143,941,335],{"class":167},[14,943,944,945,948,949,952,953,956],{},"Import from ",[38,946,947],{},"@nolag/js-sdk",". The package declares a ",[38,950,951],{},"browser"," export condition, so\nbundlers resolve the browser build for you. There is no ",[38,954,955],{},"@nolag/js-sdk/browser","\nsubpath.",[14,958,959],{},"With a token provider, the SDK also refreshes proactively: shortly before the token expires it mints a fresh one and renews the credentials over the live connection, with no disconnect and no resubscribe. Against older brokers (or on transient failures) it falls back to a quick reconnect where the server restores all subscriptions. Your application code does not need to handle expiry either way.",[14,961,962,963,966,967,970],{},"If a connection does outlive its token (for example the provider became unreachable), the server closes it with code ",[38,964,965],{},"4003"," and reason ",[38,968,969],{},"token_expired",". The SDK reacts by minting a fresh token and reconnecting immediately.",[21,972,974],{"id":973},"revoking-access","Revoking Access",[976,977,978,987,996],"ul",{},[29,979,980,983,984,986],{},[33,981,982],{},"Revoke one user:"," delete or disable their actor (by ",[38,985,51],{},") via the dashboard or REST API. New client tokens naming that actor stop working immediately, and any live connection is disconnected by the periodic revalidation check.",[29,988,989,992,993,995],{},[33,990,991],{},"Token expiry:"," a leaked client token is only useful until its ",[38,994,808],{},", at most 1 hour and typically 15 minutes.",[29,997,998,1001],{},[33,999,1000],{},"Disable a signing key:"," stops all new tokens signed with it from verifying. Connections already established run until their token expires.",[21,1003,1005],{"id":1004},"rotating-a-signing-key","Rotating a Signing Key",[14,1007,1008],{},"Multiple signing keys can be active at once, so rotation has no downtime:",[26,1010,1011,1014,1020,1023],{},[29,1012,1013],{},"Create a new signing key in the dashboard.",[29,1015,1016,1017,1019],{},"Deploy the new ",[38,1018,40],{}," to your backend.",[29,1021,1022],{},"Disable the old key. Tokens minted with it stop verifying; users pick up tokens from the new key on their next refresh.",[29,1024,1025],{},"Delete the old key once you are confident nothing still uses it.",[21,1027,1029],{"id":1028},"which-credential-where","Which Credential Where",[737,1031,1032,1048],{},[740,1033,1034],{},[743,1035,1036,1039,1042,1045],{},[746,1037,1038],{},"Credential",[746,1040,1041],{},"Lives on",[746,1043,1044],{},"Lifetime",[746,1046,1047],{},"Use for",[753,1049,1050,1067,1083,1097],{},[743,1051,1052,1058,1061,1064],{},[758,1053,1054,1055,1057],{},"Actor access token (",[38,1056,800],{},")",[758,1059,1060],{},"Your servers, devices, agents",[758,1062,1063],{},"Long-lived",[758,1065,1066],{},"Backend services, IoT, server-side SDK use",[743,1068,1069,1074,1077,1080],{},[758,1070,1071,1072,1057],{},"Signing key (",[38,1073,782],{},[758,1075,1076],{},"Your backend only",[758,1078,1079],{},"Until rotated",[758,1081,1082],{},"Minting client tokens",[743,1084,1085,1088,1091,1094],{},[758,1086,1087],{},"Client token (JWT)",[758,1089,1090],{},"Browser or mobile app",[758,1092,1093],{},"Minutes",[758,1095,1096],{},"Untrusted client connections",[743,1098,1099,1105,1107,1109],{},[758,1100,1101,1102,1057],{},"API key (",[38,1103,1104],{},"nlg_live_...",[758,1106,1076],{},[758,1108,1063],{},[758,1110,1111],{},"REST API management calls",[21,1113,1115],{"id":1114},"next-steps","Next Steps",[976,1117,1118,1124,1130],{},[29,1119,1120],{},[75,1121,1123],{"href":1122},"/docs/authentication","Authentication Overview",[29,1125,1126],{},[75,1127,1129],{"href":1128},"/docs/concepts/acl","Access Control Lists",[29,1131,1132],{},[75,1133,1135],{"href":1134},"/docs/sdks/javascript","JavaScript SDK",[1137,1138,1139],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":139,"searchDepth":178,"depth":178,"links":1141},[1142,1143,1144,1145,1148,1149,1150,1151,1152],{"id":23,"depth":178,"text":24},{"id":67,"depth":178,"text":68},{"id":124,"depth":178,"text":125},{"id":375,"depth":178,"text":376,"children":1146},[1147],{"id":734,"depth":190,"text":735},{"id":820,"depth":178,"text":821},{"id":973,"depth":178,"text":974},{"id":1004,"depth":178,"text":1005},{"id":1028,"depth":178,"text":1029},{"id":1114,"depth":178,"text":1115},"Authenticate browsers and mobile apps with short-lived JWTs minted by your backend, so long-lived access tokens never leave your servers.","md",{},"/docs/client-tokens",{"title":5,"description":1153},"docs/client-tokens","A-ZGLaZGrqNA9WeNjvjwMrDbd97Vkra-vVpTxwNxGYE",1788160342741]