{"id":742,"date":"2021-08-31T08:51:48","date_gmt":"2021-08-30T23:51:48","guid":{"rendered":"https:\/\/blog.wsd.sh\/?p=742"},"modified":"2021-08-31T08:59:06","modified_gmt":"2021-08-30T23:59:06","slug":"getting-a-token-from-chatwork-oauth","status":"publish","type":"post","link":"https:\/\/blog.wsd.sh\/?p=742","title":{"rendered":"Getting a Token from Chatwork OAuth"},"content":{"rendered":"<p>The code for this post can be found here:<a href=\"https:\/\/github.com\/wsdCollins\/Chatwork-OAuth\"> https:\/\/github.com\/wsdCollins\/Chatwork-OAuth<\/a><\/p>\n<p>This is a sample repo for connecting to and using OAuth with Chatwork. This<br \/>\nrepository will cover the scope of creating a new application, getting approval<br \/>\nor denial from a user. And concluding with getting a token on behalf of the<br \/>\nuser. This repository is not intended to cover the finer points of connecting<br \/>\nto OAuth, only to serve as a practical coding guide for connecting to<br \/>\nChatwork as an OAuth provider.<\/p>\n<h2>Step 01 &#8211; Creating a Domain Name<\/h2>\n<p>To start we will need a domain with https to host our application on. In this case we will<br \/>\nuse <em>cw.blog.wsd.sh<\/em>*. First we will add a DNS entry for the subdomain.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131271825-85231c0a-2aa4-49a7-9af8-d1b063b6001c.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131271825-85231c0a-2aa4-49a7-9af8-d1b063b6001c.png\" alt=\"Screenshot 2021-08-30 at 09-57-47 Advanced DNS\" style=\"max-width:100%;\"><\/a><\/p>\n<p>Then we need to create an Nginx configuration to serve the site.<\/p>\n<pre><code># vim \/etc\/nginx\/conf.d\/cw.blog.wsd.sh.conf\r\n--- Paste the Following content ---\r\nserver {\r\n\r\n    listen 80;\r\n    listen [::]:80;\r\n\r\n    index index.html;\r\n    server_name cw.blog.wsd.sh;\r\n\r\n    proxy_set_header Host $host;\r\n    proxy_set_header X-Forwarded-Proto $scheme; #http pr https\r\n    proxy_set_header X-Real-IP $remote_addr; #client IP address\r\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\r\n\r\n    location ^~ \/.well-known\/acme-challenge\/ {\r\n        root \/var\/www\/html;\r\n        default_type \"text\/plain\";\r\n    }\r\n\r\n    location \/ {\r\n        #try_files $uri $uri\/ =404;\r\n        proxy_pass http:\/\/localhost:4000;\r\n    }\r\n\r\n}\r\n--- Write and Quit ---\r\n<\/code><\/pre>\n<p>And then we restart nginx to implement the changes before calling certbot<br \/>\nto get a certificate from Let&#8217;s Encrypt.<\/p>\n<p>Once this is complete, you should have a valid https connection that returns<br \/>\na 502 error page because we have not set up a server to reverse proxy to.<br \/>\nWe will do that in Step 03.<\/p>\n<h2>Step 02 &#8211; Create an OAuth Client<\/h2>\n<p>Login into Chatwork, and click on the top right hand menu. Then click on &#8220;Integrations&#8221;.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131272379-69f384df-1f02-41a9-bf83-f64415b73ce8.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131272379-69f384df-1f02-41a9-bf83-f64415b73ce8.png\" alt=\"Screenshot from 2021-08-30 10-13-35\" style=\"max-width:100%;\"><\/a><\/p>\n<p>From the on the right hand menu click on OAuth, and then in the OAuth page, click on the<br \/>\nbutton that says &#8220;Create New&#8221;.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131272430-8968b842-a6c9-4c6a-915b-2158f1521ce6.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131272430-8968b842-a6c9-4c6a-915b-2158f1521ce6.png\" alt=\"Screenshot from 2021-08-30 10-14-53\" style=\"max-width:100%;\"><\/a><\/p>\n<p>Note that in the link provided for the OAuth Docmentation is an English PDF with some incomplete<br \/>\ninformation, I recoment using the Japanese documentation which can be found here: <a href=\"https:\/\/developer.chatwork.com\/ja\/\" rel=\"nofollow\">https:\/\/developer.chatwork.com\/ja\/<\/a>.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131272796-b544d8ff-a9fc-4d49-8d27-674d28de616f.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131272796-b544d8ff-a9fc-4d49-8d27-674d28de616f.png\" alt=\"Screenshot from 2021-08-30 10-20-17\" style=\"max-width:100%;\"><\/a><\/p>\n<p>From there we will enter the details for our OAuth client applicaiton. We will set the &#8220;Client Name&#8221; as &#8220;WSD Hello World&#8221;, we will include a logo for the Icon, for the &#8220;Client Type&#8221; we will select &#8221; Confidential&#8221;, and for the &#8220;Redirect URI&#8221; we will specify &#8220;<a href=\"https:\/\/cw.blog.wsd.sh\/oauth\/chatwork\" rel=\"nofollow\">https:\/\/cw.blog.wsd.sh\/oauth\/chatwork<\/a>&#8220;.<\/p>\n<p>After that we need to select the checkboxes for which permissions we will use from the application. In our case, we&#8217;re only interested in reading or writing files. So We&#8217;ll selected the scopes for basic account information, ability to read rooms, ability to read files from rooms, and ability to write files to rooms. If needed, the scopes and callbacks can be edited later. Once we&#8217;re done, we click &#8220;Create&#8221; at the bottom.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131273014-684ae378-fece-46a8-9220-79664e19c856.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131273014-684ae378-fece-46a8-9220-79664e19c856.png\" alt=\"Screenshot from 2021-08-30 10-24-34\" style=\"max-width:100%;\"><\/a><\/p>\n<p>Once that is complete, we should see this screen.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131273103-462c0b04-0a68-40f6-85c7-980c997771a1.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131273103-462c0b04-0a68-40f6-85c7-980c997771a1.png\" alt=\"Screenshot from 2021-08-30 10-26-02\" style=\"max-width:100%;\"><\/a><\/p>\n<p>Make a note of the Client ID and Client Secret at the bottom. These will be needed in the next step to authenticate our server to get tokens.<\/p>\n<h2>Step 03 &#8211; Create Application<\/h2>\n<p>First we clone this repository<\/p>\n<p>Then we need to add our client id and client seecret to the dotenv file. Replace the values<br \/>\nwith your client id and client secret from the last step (without the square brackets).<\/p>\n<p>From there we can run the server.<\/p>\n<div class=\"snippet-clipboard-content position-relative\">\n<pre><code># node index.js\r\n<\/code><\/pre>\n<p>If we open up the browser to <a href=\"https:\/\/cw.blog.wsd.sh\/\" rel=\"nofollow\">https:\/\/cw.blog.wsd.sh\/<\/a>, we should see a mockup integrations page.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131275414-9fb6dfcd-23e0-4ffe-b5d8-7bf1b84f8728.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131275414-9fb6dfcd-23e0-4ffe-b5d8-7bf1b84f8728.png\" alt=\"Screenshot from 2021-08-30 11-05-07\" style=\"max-width:100%;\"><\/a><\/p>\n<p>If we click on the &#8220;Connect&#8221; under the &#8220;Chatwork&#8221; integration, it should take us to the OAuth approval page.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131275498-275f6e84-5011-4347-bb92-5f04f860a8e8.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131275498-275f6e84-5011-4347-bb92-5f04f860a8e8.png\" alt=\"Screenshot from 2021-08-30 11-06-09\" style=\"max-width:100%;\"><\/a><\/p>\n<p>If we click &#8220;Approve&#8221;, it should take up to the callback page, where we get a token to use the application.<\/p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https:\/\/user-images.githubusercontent.com\/5259968\/131275585-a127a9f6-35ac-4003-adfd-6e52048e1232.png\"><img decoding=\"async\" src=\"https:\/\/user-images.githubusercontent.com\/5259968\/131275585-a127a9f6-35ac-4003-adfd-6e52048e1232.png\" alt=\"Screenshot from 2021-08-30 11-07-39\" style=\"max-width:100%;\"><\/a><\/p>\n<p>And we get a token that will expire in a few minutes.<\/p>\n<h2>Explanation<\/h2>\n<p>Two points to draw attention to are how we get to the OAuth accept screen, and what<br \/>\nto do after the user has chosen an action.<\/p>\n<div class=\"snippet-clipboard-content position-relative\">\n<pre><code>(function () {\r\n\r\n    'use strict'\r\n\r\n    const state = Date.now();\r\n    const url = [\r\n        'https:\/\/www.chatwork.com\/packages\/oauth2\/login.php',\r\n        '?response_type=code',\r\n        '&amp;redirect_uri=https:\/\/cw.blog.wsd.sh\/oauth\/chatwork',\r\n        '&amp;client_id=0D43MyaOxMyO6',\r\n        '&amp;scope=users.all:read rooms.info:read rooms.files:read rooms.files:write',\r\n        `&amp;state=${state}`\r\n    ].join('');\r\n\r\n    const cw_oauth = document.getElementById('cw_oauth');\r\n    cw_oauth.setAttribute('href', url);\r\n\r\n    feather.replace({ 'aria-hidden': 'true' })\r\n\r\n})();\r\n<\/code><\/pre>\n<p>In <code>public\/js\/oauth.js<\/code> is where we create the link to the Chatwork confirmation<br \/>\npage. The link to the page is <code>https:\/\/www.chatwork.com\/packages\/oauth2\/login.php<\/code><br \/>\nand we provide several GET query parameters to pass to the page.<\/p>\n<p>The <em>response_type<\/em> is code, meaning that Chatwork creates a code, and we return it<br \/>\nback to them to confirm that we received it. The <em>redirect_uri<\/em> is where we want our<br \/>\napplication to handle the response from the confirmation screen. The <em>client_id<\/em> lets<br \/>\nChatwork know specifically which application to get approval for. The <em>state<\/em> is a<br \/>\nsession id, or some other one time value to differentiate which user and which<br \/>\nsession the confirmation attempt is being made for. And <em>scope<\/em> tells the confirmation<br \/>\nscreen which permissions we want the user to allow us to use.<\/p>\n<div class=\"snippet-clipboard-content position-relative\">\n<pre><code>const express = require('express')\r\nconst fetch = require('node-fetch')\r\nconst dotenv = require('dotenv')\r\n\r\ndotenv.config();\r\n\r\nconst app = express()\r\nconst port = 4000\r\napp.use(express.static('public'))\r\n\r\napp.get('\/oauth\/chatwork', async function(req, res) {\r\n\r\n    \/\/ Create the Authentication body\r\n\r\n    const body = [ \r\n        'grant_type=authorization_code',\r\n        `code=${req.query.code}`,\r\n        'redirect_uri=https:\/\/cw.blog.wsd.sh\/oauth\/chatwork'\r\n    ].join('&amp;');\r\n\r\n    \/\/ Create Authentication Header\r\n\r\n    const basic =  `${process.env.CLIENT_ID}:${process.env.CLIENT_SECRET}`;\r\n    const base64 = Buffer.from( basic ).toString( 'base64' );\r\n\r\n    const url = 'https:\/\/oauth.chatwork.com\/token';\r\n    const params = {\r\n        method : 'POST',\r\n        headers : {\r\n            'Authorization' : `Basic ${base64}`,\r\n            'Content-Type': 'application\/x-www-form-urlencoded'\r\n        },\r\n        body : body\r\n    }\r\n\r\n    \/\/ Send Authentication Response\r\n\r\n    const ajax = await fetch( url, params );\r\n    const json = await ajax.json();\r\n    res.json( json );\r\n\r\n});\r\n\r\napp.listen(port, () =&gt; {\r\n    console.log(`Example app listening at port: ${port}`)\r\n});\r\n<\/code><\/pre>\n<p>Once the client either clicks &#8220;Allow&#8221; or &#8220;Deny&#8221; on the confirmation screen they<br \/>\nare redirected to the url we specified in GTE query parameters for the<br \/>\nconfirmation screen. In this case since we&#8217;re using Express, I used a path<br \/>\nwithout an extension and doesn&#8217;t represent a folder to designate that callback<br \/>\nis being executed as a server-side process as opposed to a page that is displayed<br \/>\nto the user.<\/p>\n<p>Admittedly this callback doesn&#8217;t handle the possibility of getting a &#8220;Deny&#8221; reply.<br \/>\nIn reality, we should be checking the response and replying accordingly. In this case<br \/>\nwe wanted to focus on how we accept the token on the condition the user clicks<br \/>\non &#8220;Allow&#8221;. And we see that in the body which provides three values.<\/p>\n<p>The two values that are passed to us as GET query parameters, is the state value we provided<br \/>\nand a code value. The first value we reply with is <em>grant_type<\/em>, which has a fixed value of<br \/>\n&#8220;authorization_code&#8221;. For the <em>code<\/em> we supply the GET query parameter that was sent to us.<br \/>\nAnd last is the <em>redirect_uri<\/em>, which shouldn&#8217;t be needed as is not required as we&#8217;re already<br \/>\ndone with that part, but I found if I didn&#8217;t supply it, I got errors that the value didn&#8217;t<br \/>\nmatch or something.<\/p>\n<p>From there we need to supply our secret to confirm to Chatwork OAuth, that it really is<br \/>\nus, and not somebody else using our client ID. The way we do this is a twist on<br \/>\n&#8220;Basic Authentication&#8221;, which really just means authenticating with &#8220;username:password&#8221;.<br \/>\nFor the username we supply the Client Id from our OAuth settings page, followed by a colon<br \/>\nand the password is the Client Secret from our OAuth settings page.<\/p>\n<p>We then take the string of &#8220;[Client_Id]:[Client_Secret]&#8221; and encode that as a base64<br \/>\nstring. We then need to supply that as a header. The header value is &#8220;Authorization&#8221;, and<br \/>\nthe value is the string &#8220;Basic&#8221; followed by a space and the base64 string of our<br \/>\nclient id and client secret. The content type is <code>application\/x-www-form-urlencoded<\/code>,<br \/>\nand the body is our form-urlencoded list of arguments made in the body.<\/p>\n<p>We send this to Chatwork to get a token as a response. And that allows us to send<br \/>\nAPI requests on behalf of the user, but we still have some questions to fill in<br \/>\nfrom here. How do we renew a token? How do we get a token on login? And how do we<br \/>\nstructure our app to recognize the token exists to act accordingly?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The code for this post can be found here: https:\/\/github.com\/wsdCollins\/Chatwork-OAuth This is a sample repo f&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/742"}],"collection":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=742"}],"version-history":[{"count":5,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/742\/revisions"}],"predecessor-version":[{"id":747,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=\/wp\/v2\/posts\/742\/revisions\/747"}],"wp:attachment":[{"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=742"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=742"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.wsd.sh\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=742"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}