{"__v":8,"_id":"564ba9403025942100a8e708","category":{"project":"53fe6dc5addab8973c1af267","version":"53fe6dc5addab8973c1af26a","_id":"564fb3a59b4fab1700187518","pages":[],"__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-11-20T23:58:29.588Z","from_sync":false,"order":1,"slug":"integration-guide","title":"Integration Guide"},"parentDoc":null,"project":"53fe6dc5addab8973c1af267","user":"53fe70beaddab8973c1af273","version":{"__v":19,"_id":"53fe6dc5addab8973c1af26a","project":"53fe6dc5addab8973c1af267","createdAt":"2014-08-27T23:46:13.941Z","releaseDate":"2014-08-27T23:46:13.941Z","categories":["53fe6dc5addab8973c1af26b","53fe71a2addab8973c1af276","53fe7d89addab8973c1af2b0","53fe7d8daddab8973c1af2b1","53fe836faddab8973c1af2ce","53ff9a4823a37e1d5cebafe1","53ff9e3723a37e1d5cebaff7","53ffaca523a37e1d5cebb039","53ffad2e23a37e1d5cebb03c","5400c7d2ec93b29b61d4f7be","5400f0e1ec93b29b61d4f7dd","54d5636323010a0d001aca81","54d565c1276f8e0d00feab54","54ff40532882a10d00546927","556606d25561af0d008208b7","558c91900b236c2500d37c9a","56180a14f8c9632100ac7599","564fb3a59b4fab1700187518","5702e2d2f2d6f336005e901f"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0"},"updates":["5661d2b1f183880d004d319f","56faf4315d11790e00c945e0"],"next":{"pages":[],"description":""},"createdAt":"2015-11-17T22:25:04.601Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":3,"body":"[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Tutorial Requirements\",\n  \"body\": \"1. In order to finish this tutorial, you'll need to have already handled [connecting existing users](/v1.0/docs/connect-existing-users).\\n2. If you are testing your logout hook on a localhost, see [this guide](https://docs.getclef.com/docs/testing-logout-hooks).\"\n}\n[/block]\nWith Clef, your users can log out from your website (and other websites they use Clef with) by tapping the \"Log out now\" button on their phone or by setting a timer. \n\nThis paradigm makes securely logging in to different websites a lot easier for your users, since they no longer have to individually log out of all the sites they use or type tokens for each website when they want to log back in. \n\nWhen a user logs out from their phone, you'll **receive a webhook from Clef** indicating that a certain user logged out. You'll perform a simple handshake with Clef to get that user's Clef ID, and use that to query for the user. Once you've queried for the user in your database, you'll set a timestamp on the user, which you'll check on any further page loads. \n\n# Handling the logout webhook\n\nTo receive webhooks sent by Clef, you'll need to set your application's **logout hook URL** in the [Clef developer dashboard](https://getclef.com/user/login). \n\n## Getting the user's Clef ID\n\nWhen a user logs out, Clef triggers a POST to your logout URL with this body, encoded as `application/x-www-form-urlencoded`:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"logout_token=32d730c4913ddef1bb89b2ab60172108\",\n      \"language\": \"text\",\n      \"name\": \"Logout token request body\"\n    }\n  ]\n}\n[/block]\nTo determine which user logged out, you'll need to exchange this `logout_token` for the user's Clef ID. The easiest way to implement this handshake is by using one of our [API Libraries](http://docs.getclef.com/v1.0/docs/api-libraries). \n\nIn your webserver's logout hook endpoint, grab the `logout_token` from the POST body. From there, it's easy to exchange the code for a user's Clef ID: \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"import clef\\nclef.initialize(app_id=YOUR_APP_ID, app_secret=YOUR_APP_SECRET)\\n\\n# In your logout hook endpoint:\\n\\nlogout_token = request.form.get(\\\"logout_token\\\")\\nclef_id = clef.get_logout_information(logout_token=logout_token)\",\n      \"language\": \"python\"\n    },\n    {\n      \"code\": \"// In your logout hook endpoint:\\n\\nvar token = req.body.logout_token;\\nclef.getLogoutInformation({logoutToken: token}, function(err, clefID){\\n\\t// We'll do more stuff here in just a sec\\n});\",\n      \"language\": \"javascript\",\n      \"name\": \"Node\"\n    },\n    {\n      \"code\": \"<?php\\n\\t// In logout.php (or something similarly named)\\n\\t\\\\Clef\\\\Clef::initialize(APP_ID, APP_SECRET);\\n\\n\\tif(isset($_POST['logout_token'])) {\\n    try {\\n      $clef_id = \\t\\\\Clef\\\\Clef::get_logout_information($_POST[\\\"logout_token\\\"]);\\n    } catch (Exception $e) {\\n      die(json_encode(array('error' => $e->getMessage())));\\n    }\\n  }\\n?>\",\n      \"language\": \"php\"\n    }\n  ]\n}\n[/block]\n# Logging users out from your application\n\nNow that you know the user's Clef ID, it's time to log them out from your application.\n\nSince Clef sends a webhook, you won't have access to the user's browser session like you would during a browser request. Instead, we'll set a property, `logged_out_at`, on the user in your database. In addition, when a user does a *login*, we'll set another property, `logged_in_at` in the user's browser session. When a user views any authenticated page on your website, we'll compare those two properties and determine whether we need to clear a user's browser session, thus logging them out.\n\n## Setting `logged_out_at` in your database\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Updating your User model\",\n  \"body\": \"Before you begin, you'll need to add a `logged_out_at` column to your user table:\\n\\n```\\nALTER TABLE user ADD logged_out_at DATETIME;\\n```\\n\\nYou'll use `logged_out_at` to determine if a user has logged out on subsequent page loads.\"\n}\n[/block]\nSince we now have the user's Clef ID, let's look the user up in your database and set a timestamp recording when they logged out. Still in your logout webhook URL:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"user = User.query.filter_by(clef_id=clef_id)\\nuser.logged_out_at = datetime.datetime.now()\\nuser.save()\\nreturn 'ok' # Return a 200 response to Clef's logout webhook request\",\n      \"language\": \"python\"\n    },\n    {\n      \"code\": \"// Inside the callback function from above:\\nUser.find({where: {clefID: clefID}}).then(function(user){\\n  user.updateAttributes({\\n    loggedOutAt: Date.now()\\n  }).then(function() {\\n    res.send('ok');\\n  });\",\n      \"language\": \"javascript\",\n      \"name\": \"Node\"\n    },\n    {\n      \"code\": \"<?php\\n\\t// In logout.php (or something similarly named)\\n\\t\\\\Clef\\\\Clef::initialize(APP_ID, APP_SECRET);\\n\\n\\tif(isset($_POST['logout_token'])) {\\n    try {\\n      $clef_id = \\t\\\\Clef\\\\Clef::get_logout_information($_POST[\\\"logout_token\\\"]);\\n      update_logged_out_at($clef_id, time(), $mysql);\\n    } catch (Exception $e) {\\n      die(json_encode(array('error' => $e->getMessage())));\\n    }\\n  }\\n\\n\\t// ...\\n\\n\\t// This example uses raw SQL, but the concept is the same with an ORM \\n\\t// like Propel or Doctrine — query by clef_id, and update the\\n\\t// value of `logged_out_at` with a new time.\\n\\tfunction update_logged_out_at($id, $time, $mysql) {\\n    $query = \\\"UPDATE \\\". DB_NAME .\\\".users SET logged_out_at={$time} WHERE clef_id='{$id}';\\\";\\n    if (!$result = $mysql->query($query)) {\\n        die('There was an error running the query: ' . $mysql->error);\\n    }\\n    return $result;\\n\\t}\\n?>\",\n      \"language\": \"php\"\n    }\n  ]\n}\n[/block]\n## Setting `logged_in_at` during login\n\nNow let's handle the other half of the logout equation. When a user logs in with Clef, in addition to setting a flag in their browser session to indicate they're logged in, you'll also want to set a `logged_in_at` property. Following along with our [login documentation](http://docs.getclef.com/v1.0/docs/log-your-users-in#section-logging-users-in), adjust this line: \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"session['user_id'] = user.id\\nsession['logged_in_at'] = datetime.datetime.now()\",\n      \"language\": \"python\"\n    },\n    {\n      \"code\": \"req.session.userID = user.id;\\nreq.session.loggedInAt = Date.now();\",\n      \"language\": \"javascript\",\n      \"name\": \"Node\"\n    },\n    {\n      \"code\": \"$_SESSION['user_id'] = $user->id;\\n$_SESSION['logged_in_at'] = time();\",\n      \"language\": \"php\"\n    }\n  ]\n}\n[/block]\nNow, when your users log in with Clef, we have a way of storing what time they logged in.\n\n## Putting the pieces together\n\nYou probably have a function, middleware, or decorator which takes a browser session and checks whether there is a valid user. We're going to add code there that checks the values of `logged_in_at` and `logged_out_at`. If `logged_out_at` is more recent than `logged_in_at`, then we know the user has logged out with Clef and we should clear their session.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"def logged_in(view):\\n    \\\"\\\"\\\"\\n    Decorator that checks whether a user is currently logged in.\\n    If a user is logged in it provides the user object.\\n    \\\"\\\"\\\"\\n    :::at:::functools.wraps(view)\\n    def decorated_view(*args, **kwargs):\\n        user_id = session.get('user', -1)\\n        user = User.query.get(user_id)\\n\\n\\t\\t\\t\\t# ---------- This is the code to add ---------\\n        logged_in_at = session.get('logged_in_at')\\n        # Check whether the user previously logged out\\n        if not user or user.logged_out_at > logged_in_at:\\n            session.clear()\\n            user = None\\n\\n        return view(user=user, *args, **kwargs)\\n    return decorated_view\\n\",\n      \"language\": \"python\"\n    },\n    {\n      \"code\": \"/**\\n * This middleware function is used to check whether the user has logged out\\n * with Clef already, and if so, destroys their session in the browser,\\n * logging them out.\\n */\\napp.use(function(req, res, next) {\\n  if (req.session.user == undefined) { return next(); }\\n\\n  User.find({ where: { id: req.session.user.id } }).then(function(user) {\\n    \\n    // ---------- This is the code to add ---------\\n    if (!user || user.loggedOutAt > req.session.loggedInAt) {\\n      req.session.destroy();\\n      res.redirect('/');      \\n    } else {\\n\\t\\t\\tnext():\\n    }\\n  })\\n});\",\n      \"language\": \"javascript\",\n      \"name\": \"Node\"\n    },\n    {\n      \"code\": \"<?php\\n\\n// In the function where you typically check for an authenticated user: \\n$user = get_user($_SESSION['user_id'], $mysql);\\nif (!$user) header(\\\"Location: index.php\\\");\\n\\n// ---------- This is the code to add ---------\\nif (isset($user->logged_out_at)) {\\n  if (!isset($_SESSION['logged_in_at']) || user->logged_out_at > $_SESSION['logged_in_at']) {\\n    session_destroy();\\n    header('Location: index.php');\\n  }\\n}\\n\\n?>\",\n      \"language\": \"php\"\n    }\n  ]\n}\n[/block]\n# Next up\n\n**You did it!** \n\nIf you've been following along with our four tutorials, you've done everything you need to integrate Clef. You can now go live with your integration by toggling the \"Go live\" checkbox in [Clef's developer dashboard](https://getclef.com/user/login).","excerpt":"","slug":"log-your-users-out","type":"basic","title":"Log your users out"}

Log your users out


[block:callout] { "type": "warning", "title": "Tutorial Requirements", "body": "1. In order to finish this tutorial, you'll need to have already handled [connecting existing users](/v1.0/docs/connect-existing-users).\n2. If you are testing your logout hook on a localhost, see [this guide](https://docs.getclef.com/docs/testing-logout-hooks)." } [/block] With Clef, your users can log out from your website (and other websites they use Clef with) by tapping the "Log out now" button on their phone or by setting a timer. This paradigm makes securely logging in to different websites a lot easier for your users, since they no longer have to individually log out of all the sites they use or type tokens for each website when they want to log back in. When a user logs out from their phone, you'll **receive a webhook from Clef** indicating that a certain user logged out. You'll perform a simple handshake with Clef to get that user's Clef ID, and use that to query for the user. Once you've queried for the user in your database, you'll set a timestamp on the user, which you'll check on any further page loads. # Handling the logout webhook To receive webhooks sent by Clef, you'll need to set your application's **logout hook URL** in the [Clef developer dashboard](https://getclef.com/user/login). ## Getting the user's Clef ID When a user logs out, Clef triggers a POST to your logout URL with this body, encoded as `application/x-www-form-urlencoded`: [block:code] { "codes": [ { "code": "logout_token=32d730c4913ddef1bb89b2ab60172108", "language": "text", "name": "Logout token request body" } ] } [/block] To determine which user logged out, you'll need to exchange this `logout_token` for the user's Clef ID. The easiest way to implement this handshake is by using one of our [API Libraries](http://docs.getclef.com/v1.0/docs/api-libraries). In your webserver's logout hook endpoint, grab the `logout_token` from the POST body. From there, it's easy to exchange the code for a user's Clef ID: [block:code] { "codes": [ { "code": "import clef\nclef.initialize(app_id=YOUR_APP_ID, app_secret=YOUR_APP_SECRET)\n\n# In your logout hook endpoint:\n\nlogout_token = request.form.get(\"logout_token\")\nclef_id = clef.get_logout_information(logout_token=logout_token)", "language": "python" }, { "code": "// In your logout hook endpoint:\n\nvar token = req.body.logout_token;\nclef.getLogoutInformation({logoutToken: token}, function(err, clefID){\n\t// We'll do more stuff here in just a sec\n});", "language": "javascript", "name": "Node" }, { "code": "<?php\n\t// In logout.php (or something similarly named)\n\t\\Clef\\Clef::initialize(APP_ID, APP_SECRET);\n\n\tif(isset($_POST['logout_token'])) {\n try {\n $clef_id = \t\\Clef\\Clef::get_logout_information($_POST[\"logout_token\"]);\n } catch (Exception $e) {\n die(json_encode(array('error' => $e->getMessage())));\n }\n }\n?>", "language": "php" } ] } [/block] # Logging users out from your application Now that you know the user's Clef ID, it's time to log them out from your application. Since Clef sends a webhook, you won't have access to the user's browser session like you would during a browser request. Instead, we'll set a property, `logged_out_at`, on the user in your database. In addition, when a user does a *login*, we'll set another property, `logged_in_at` in the user's browser session. When a user views any authenticated page on your website, we'll compare those two properties and determine whether we need to clear a user's browser session, thus logging them out. ## Setting `logged_out_at` in your database [block:callout] { "type": "warning", "title": "Updating your User model", "body": "Before you begin, you'll need to add a `logged_out_at` column to your user table:\n\n```\nALTER TABLE user ADD logged_out_at DATETIME;\n```\n\nYou'll use `logged_out_at` to determine if a user has logged out on subsequent page loads." } [/block] Since we now have the user's Clef ID, let's look the user up in your database and set a timestamp recording when they logged out. Still in your logout webhook URL: [block:code] { "codes": [ { "code": "user = User.query.filter_by(clef_id=clef_id)\nuser.logged_out_at = datetime.datetime.now()\nuser.save()\nreturn 'ok' # Return a 200 response to Clef's logout webhook request", "language": "python" }, { "code": "// Inside the callback function from above:\nUser.find({where: {clefID: clefID}}).then(function(user){\n user.updateAttributes({\n loggedOutAt: Date.now()\n }).then(function() {\n res.send('ok');\n });", "language": "javascript", "name": "Node" }, { "code": "<?php\n\t// In logout.php (or something similarly named)\n\t\\Clef\\Clef::initialize(APP_ID, APP_SECRET);\n\n\tif(isset($_POST['logout_token'])) {\n try {\n $clef_id = \t\\Clef\\Clef::get_logout_information($_POST[\"logout_token\"]);\n update_logged_out_at($clef_id, time(), $mysql);\n } catch (Exception $e) {\n die(json_encode(array('error' => $e->getMessage())));\n }\n }\n\n\t// ...\n\n\t// This example uses raw SQL, but the concept is the same with an ORM \n\t// like Propel or Doctrine — query by clef_id, and update the\n\t// value of `logged_out_at` with a new time.\n\tfunction update_logged_out_at($id, $time, $mysql) {\n $query = \"UPDATE \". DB_NAME .\".users SET logged_out_at={$time} WHERE clef_id='{$id}';\";\n if (!$result = $mysql->query($query)) {\n die('There was an error running the query: ' . $mysql->error);\n }\n return $result;\n\t}\n?>", "language": "php" } ] } [/block] ## Setting `logged_in_at` during login Now let's handle the other half of the logout equation. When a user logs in with Clef, in addition to setting a flag in their browser session to indicate they're logged in, you'll also want to set a `logged_in_at` property. Following along with our [login documentation](http://docs.getclef.com/v1.0/docs/log-your-users-in#section-logging-users-in), adjust this line: [block:code] { "codes": [ { "code": "session['user_id'] = user.id\nsession['logged_in_at'] = datetime.datetime.now()", "language": "python" }, { "code": "req.session.userID = user.id;\nreq.session.loggedInAt = Date.now();", "language": "javascript", "name": "Node" }, { "code": "$_SESSION['user_id'] = $user->id;\n$_SESSION['logged_in_at'] = time();", "language": "php" } ] } [/block] Now, when your users log in with Clef, we have a way of storing what time they logged in. ## Putting the pieces together You probably have a function, middleware, or decorator which takes a browser session and checks whether there is a valid user. We're going to add code there that checks the values of `logged_in_at` and `logged_out_at`. If `logged_out_at` is more recent than `logged_in_at`, then we know the user has logged out with Clef and we should clear their session. [block:code] { "codes": [ { "code": "def logged_in(view):\n \"\"\"\n Decorator that checks whether a user is currently logged in.\n If a user is logged in it provides the user object.\n \"\"\"\n @functools.wraps(view)\n def decorated_view(*args, **kwargs):\n user_id = session.get('user', -1)\n user = User.query.get(user_id)\n\n\t\t\t\t# ---------- This is the code to add ---------\n logged_in_at = session.get('logged_in_at')\n # Check whether the user previously logged out\n if not user or user.logged_out_at > logged_in_at:\n session.clear()\n user = None\n\n return view(user=user, *args, **kwargs)\n return decorated_view\n", "language": "python" }, { "code": "/**\n * This middleware function is used to check whether the user has logged out\n * with Clef already, and if so, destroys their session in the browser,\n * logging them out.\n */\napp.use(function(req, res, next) {\n if (req.session.user == undefined) { return next(); }\n\n User.find({ where: { id: req.session.user.id } }).then(function(user) {\n \n // ---------- This is the code to add ---------\n if (!user || user.loggedOutAt > req.session.loggedInAt) {\n req.session.destroy();\n res.redirect('/'); \n } else {\n\t\t\tnext():\n }\n })\n});", "language": "javascript", "name": "Node" }, { "code": "<?php\n\n// In the function where you typically check for an authenticated user: \n$user = get_user($_SESSION['user_id'], $mysql);\nif (!$user) header(\"Location: index.php\");\n\n// ---------- This is the code to add ---------\nif (isset($user->logged_out_at)) {\n if (!isset($_SESSION['logged_in_at']) || user->logged_out_at > $_SESSION['logged_in_at']) {\n session_destroy();\n header('Location: index.php');\n }\n}\n\n?>", "language": "php" } ] } [/block] # Next up **You did it!** If you've been following along with our four tutorials, you've done everything you need to integrate Clef. You can now go live with your integration by toggling the "Go live" checkbox in [Clef's developer dashboard](https://getclef.com/user/login).