<p>I'm currently writing an API in Go and have been racking my brain over how to do authentication/authorization correctly and securely. </p>
<p><strong>As I understand it, this is how it goes:</strong></p>
<ul>
<li>New user registers for account via <code>api/user/register</code> endpoint (or <code>api/user/login</code> for existing users)</li>
<li>Server receives request and checks that username is unique, etc. After that, it issues (if all is well) an <strong>access token</strong> and <strong>refresh token</strong>, both signed for added security.</li>
<li>The client app receives the tokens and stores them in the browser cookie (or local/session Storage) and makes sure to send them securely over HTTPS in any subsequent requests to the API.</li>
<li>When receiving requests to protected routes, the server checks the access token's expiry date, and if expired, will check the refresh token's validity in the database. If it's invalid, ask for reauthentication clientside. Otherwise, reissue a new access token.</li>
</ul>
<p>My questions are regarding the steps dealing with <strong>refresh tokens</strong>.</p>
<p>I am also writing the client application (in React); it won't be a public API anyone can wrap with their own client. I simply am writing the backend as an API for the client app. </p>
<ul>
<li>Should I still use refresh tokens? </li>
<li>Do I need an <code>api/auth/token</code> route? I keep reading about them in implementation examples and I feel like I can just have some helper functions to query the database and reissue tokens in my backend code instead of having to query another endpoint to do so.</li>
</ul>
<p>Sorry if they're dumb questions, but I've been poring over page after page detailing the auth spec, and the subtle differences from page to page are leaving me confused and unsure of what is truly "best practice" in production.</p>
<hr/>**评论:**<br/><br/>ejcx: <pre><p>My advice to you. Mark the cookies as Secure if you want them to only be sent over HTTPS. Cookie flags are kind of hand-wavy in what they actually provide, but it's helpful.</p>
<p>Also, IMO you are over complicating things. Cookies already have mechanisms to be expired client side, and server side they can be expired as well. Issuing lots of access tokens and a long-lived refresh token is complicated, and not much more secure than just issuing a longer lived access token. The difference is negligible in exchange for lots of work. You are better off making it easy for users to revoke and issue new access tokens.</p></pre>wtfridge: <pre><p>Yeah that's true, I suppose. But I want to see if I can implement it. But if it doesn't work out I could try just using access tokens with secure cookies.</p></pre>luckyleprechaun98: <pre><p>Refresh tokens make some sense in Oauth applications, but for an API that you control they aren't needed. I'm a big fan of JWTs in certain contexts. However, they don't offer a ton of benefits for typical APIs that need logout functionality. You have to keep a blacklist server-side and then it doesn't offer much over regular session cookies. </p>
<p>I think it's always better to keep it simple. Session cookies for normal API stuff so you have the ability to invalidate them server-side. JWTs for special stuff like password reset links, magic links that do auto-login, long-term access to services that don't need logouts, and other stuff like that. </p></pre>softwaregav: <pre><p>I was in the same boat as you. Here's the approach I took:</p>
<p>1) User must hit <code>/api/user/register</code> or <code>api/user/login</code>. Upon success, the user will receive an <em>access</em> token and a <em>refresh</em> token.</p>
<p>2) Subsequent requests must include the <em>access</em> token. If the endpoint is protected, the access token will be inspected.</p>
<ul>
<li>a) Expiration check - expired token results in returning a 401</li>
<li>b) Signature check - a token that has been tampered with results in returning a 401</li>
<li>c) Authorizaion check - ok, I see you are who you say you are, but are you allowed to be here? if not, we return a 401</li>
<li>d) Now we can complete the request</li>
</ul>
<p>3) The user hits <code>api/user/logout</code>, so we expire or remove their <em>refresh</em> token.</p>
<p>On the client side, if you are receiving a 401, it means that case a, b or c has probably happened. Here's how to handle that:</p>
<ul>
<li><p>a) Our access token is expired, so let's get a new one. Send a request to <code>/api/user/token</code> with our <em>refresh</em> token. The server will verify that it actually issued the provided refresh token. If it did, the server can form a new <em>access</em> token and pass it back to the client.</p></li>
<li><p>b) Something about the token is malformed. We can take the same steps as a) and try to get a new <em>access</em> token.</p></li>
<li><p>c) We need different privileges to access the desired endpoint.</p></li>
</ul>
<p>To sum this up:
<em>Access</em> tokens are very short lived (I usually have them last ~20 minutes). When these expire, we send in the <em>refresh</em> token to get a new <em>access</em> token, which allows the user to <em>access</em> particular endpoints. <em>Refresh</em> tokens will essentially live for the duration of the user being 'signed in'. These can have a long expiration (say, a few months), or they could not expire at all and you just remove it upon a user logging out. It is not unreasonable to have more than one good <em>refresh</em> token stored for a single user if you are using one per device per user.</p>
<p><strong>EDIT</strong></p>
<p>Some of the points I've made are assuming you're using JWTs for your access tokens.</p></pre>wtfridge: <pre><p>Okay that definitely helps a lot in explaining things.</p>
<p>Just one more question, in 3a of your post, if the refresh token is expired, we just ask them to reauthenticate via a login page right?</p></pre>softwaregav: <pre><p>That's kind of up to you. You could redirect to a login page, or you could leave it up to the client to know that if the refresh token isn't valid they need to re-authenticate.</p></pre>wtfridge: <pre><p>Hmm okay. Also, for checking validity of a refresh token, it could be something as simple as checking if the refresh token's string exists in a table in the database, right?</p></pre>softwaregav: <pre><p>Yep. I have a separate table for them because I will issue one per person per device. So, I check if it exists, pull back the user info based on the UserID foreign key, and form an access token with that info. I would make sure there is a unique constraint on the actual refresh token column. You wouldn't want to accidentally (though it's not likely) have two identical refresh tokens issued to two different people.</p></pre>wtfridge: <pre><p>Good point with the last bit on unique identifiers. I think I have enough to start coding it up though, thanks so much for the clarification </p></pre>softwaregav: <pre><p>No problem. I know how much time I spent stuck in a trance or pacing around my apartment trying to wrap my head around how to do this stuff, so I like to help out when I can. Shoot me a message if you need help with anything else. Happy coding</p></pre>wtfridge: <pre><p>Yeah, I abandoned this project 6ish months ago because I was intimidated by all this. But I'm determined to push through this time. </p>
<p>I'll definitely let you know if I have any other questions, which I probably will, haha.</p></pre>twotower: <pre><p>Try JWT.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
0 回复
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传