API Authentication flow

polaris · · 382 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;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&#39;s expiry date, and if expired, will check the refresh token&#39;s validity in the database. If it&#39;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&#39;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&#39;re dumb questions, but I&#39;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 &#34;best practice&#34; 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&#39;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&#39;s true, I suppose. But I want to see if I can implement it. But if it doesn&#39;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&#39;t needed. I&#39;m a big fan of JWTs in certain contexts. However, they don&#39;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&#39;t offer much over regular session cookies. </p> <p>I think it&#39;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&#39;t need logouts, and other stuff like that. </p></pre>softwaregav: <pre><p>I was in the same boat as you. Here&#39;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&#39;s how to handle that:</p> <ul> <li><p>a) Our access token is expired, so let&#39;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 &#39;signed in&#39;. 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&#39;ve made are assuming you&#39;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&#39;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&#39;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&#39;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&#39;t want to accidentally (though it&#39;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&#39;m determined to push through this time. </p> <p>I&#39;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

382 次点击  
加入收藏 微博
0 回复
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传