-
-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple token suggestion #30
Comments
Ya, that's a good idea, I can add that in. Bit swamped for now with some projects so will look into it next week. |
Hey, thx again for the suggestion. So I've update the scheme as per your suggestion. In the response it will go through the array of token auth schemes and attempt one until it finds a token there and use that. For the request it will just default to using the name and authType from the first scheme in the array. Thing is it will always do it as a header. Not sure it really makes sense otherwise so I will leave it like that for now. One thing I'm a bit unclear on in your situation. Is it attempting to validate all those schemes per request, or just to find the first one that works? |
This diagram shows a more technical response. |
In short, I need to capture the API's 3 response headers, update my localStorage with those values, then use them in my next request. I login to the API just like you would in Laravel via a single email/password post request. The response back on a successful authentication includes three tokens in the response headers: uid, client, access-token. Every time I make a request to the API, I need to use those values in the request header. The access-token changes on each response. Based on the existing rails session and how long I sit idle vs how long my token is configured to expire, the client and uid tokens may change also. So because i am lazy I just grab, store, inject like this: // auth.js
export default {
tokens: [{
name: 'access-token',
type: 'bearer',
header: true,
}, {
name: 'uid',
header: true,
}, {
name: 'client',
header: true,
}],
interceptResponse(httpHeaderResponse){
this.tokens.forEach(token => {
if (token.header && httpHeaderResponse.has(token.name)){
window.localStorage.setItem(token.name,httpHeaderResponse.get(token.name))
}
})
},
// ... more auth stuff
}
... // main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource'
import auth from './auth'
Vue.use(VueRouter)
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
// modify request
auth.tokens.forEach(token => {
if (window.localStorage.getItem(token.name)) {
let tokenValue = localStorage.getItem(token.name)
if (token.type) {
tokenValue = token.type + ' ' + tokenValue
}
request.headers.set(token.name, tokenValue)
}
})
// continue to next interceptor
next(response => { auth.interceptResponse(response.headers) })
}) |
Ok, so actually in this case I don't think I would implement this directly into the plugin. Unless there is some actual spec for it (is there?). So best approach would be to just add a custom auth scheme which is already supported. Here is a sample from the current bearer one:
There is the |
Ok so with the new ability to provide an array of tokens in the config, I just define the authType as my own custom scheme like authDevise: ... Set all the tokens to that type, extend your plugin, loop all defined tokens and get/store/set each request. By the way, the code I have in my last post is working with your plugin. I just configure the one main token with your plugin and then add my own 2 interceptors. So far it's been working fine. |
Ya, well if it works, that's great then :P If there is some official spec for this authentication scheme let me know. Otherwise I will close this out. |
I've run into a similar situation. A rails API using "devise_token_auth" gem will send back data similar to what @cjcrawford was dealing with and I've actually used his code to get vue-auth working with the API. However I'd like to use the custom scheme mentioned here. I was wondering if you could point me in the direction of how I'd actually configure vue-auth with a custom scheme similar to bearerAuth. In terms of standards, the author of devise_token_auth talks about that here https://github.com/lynndylanhurley/devise_token_auth#token-header-format |
So i see: "The authentication headers consists of the following params:" access-token, client, expiry, uid These are all to be stored locally and are updated on every request? |
That's correct. After some more testing last night I'm guessing this presents a problem for vue-auth as its expecting to get/set one header per request. |
Yea, I will have to rethink this a bit. On Nov 3, 2016 18:13, "Justin Mullis" notifications@github.com wrote:
|
Ok, I've made an update here and simplified the auth so that it's much easier to add auth drivers. Make sure to get the latest
Basically you get the Let me know if that works or makes sense at all. |
Note you should also be able to use the internal
|
So can I do something like this in the config options? edit: fixed sample config code to something that might actually compile... : // vue-auth config example for devise on rails API
deviseTokens: ['Token-Type', 'Access-Token', 'Client', 'Uid', 'Expiry'],
authType: 'devise',
deviseAuth: {
request(req, token){
let tokens = token.split(';');
this.options.deviseTokens.forEach((tokenName, index) => {
if (tokens[index]){
req.headers.set(tokenName, tokens[index]);
}
})
},
response(res) {
let headers = this.options._getHeaders.call(this, res);
let return_val = [];
this.options.deviseTokens.forEach((tokenName, index) => {
if (headers[tokenName]){
return_val.push(headers[tokenName]);
}
})
return return_val.join(';');
}
} |
Ya, in theory :p. I may just add that as a standard auth method. Is it On Nov 4, 2016 16:10, "Conan Crawford" notifications@github.com wrote:
|
I'll have some time this weekend to test it against the rails API. v1.4.0-beta right? |
I'll also check this weekend. Really appreciate the changes by the way! |
This is working! @cjcrawford flip the index and tokenName arguments on forEach. Also on my install the token names were case sensitive. deviseTokens: ['Token-Type', 'Access-Token', 'Client', 'Uid', 'Expiry'], Thank you guys! Just for reference I'm using |
I'll fix the code to line up with your fixes and we should have a good prototype for a devise auth. I'll be testing against my rails server later today, too. |
@cjcrawford yes v1.4.0-beta. |
Awesome @cjcrawford and @NonAdmin. @cjcrawford would you like to submit a PR or should I go ahead and put this in myself? |
I can confirm that the fixed code above is working for me on my rails API. I would rather you implement it your own way with your own patterns. Here's my vue-auth config with standard devise endpoints. import Vue from 'vue'
import VueRouter from 'vue-router'
const VueAuth = require('@websanova/vue-auth') // "@websanova/vue-auth": "^1.4.0-beta"
const apiEndPoint = 'https://API.YOURDOMAIN.COM/api/v1'
Vue.use(VueRouter)
Vue.router = new VueRouter({
//... config
})
Vue.use(VueAuth, {
router: Vue.router,
deviseTokens: ['Token-Type', 'Access-Token', 'Client', 'Uid', 'Expiry'],
authType: 'devise',
deviseAuth: {
request(req, token){
let tokens = token.split(';')
this.options.deviseTokens.forEach((tokenName, index) => {
if (tokens[index]){
req.headers.set(tokenName, tokens[index])
}
})
},
response(res) {
let headers = this.options._getHeaders.call(this, res)
let return_val = []
this.options.deviseTokens.forEach((tokenName) => {
if (headers[tokenName]){
return_val.push(headers[tokenName])
}
})
return return_val.join(';')
}
},
registerData: {url: apiEndPoint + '/auth', method: 'POST', redirect: '/account'},
loginData: {url: apiEndPoint + '/auth/sign_in', method: 'POST', redirect: '/account'},
logoutData: {url: apiEndPoint + '/auth/sign_out', method: 'DELETE', redirect: '/account/login', makeRequest: true},
refreshData: {url: apiEndPoint + '/auth/validate_token', method: 'GET', atInit: true},
fetchData: {url: apiEndPoint + '/auth/validate_token', method: 'GET'},
}) |
Ok, I've added this as a standard auth method. |
I'll have to check out the new version, but I'm running into issues trying to use "rememberMe" on login. It works initially and I can close and re-open the browser, move around my app (currently just the login page and a single route that pulls an index from the API). However eventually the token info that vue-auth sends to the API gets rejected. I can't seem to figure out why. I've tried in Safari and Chrome, and I even downloaded the "demo" API for the devise_token_auth gem and observed the same behavior. It almost seems like sometimes vue-auth isn't recording the latest token information to local storage, and therefore tries to use an old token which the API rejects. Also once this starts happening vue-auth stops working at all. Login goes through but then it immediately gets rejected by the API on the next request (usually fetch data). If I clear the cache and start again it will work ok for 5-10 requests before bugging out again. |
Ok I can confirm that is still an issue in |
I think this is more than likely some sort of issue with devise_token_auth. It has a setting where requests made within 5 seconds of each other are considered a "batch" and the token doesn't change. However this seems to have caused some outstanding issues for other users and they're seeing the same random logouts I'm seeing. |
Ok, I think I've gotten to the bottom of this. It is due to how devise_token_auth handles "batches". More info here. Sometimes XHR responses come back as 304, instructing the browser to pull cached results for a request. This will eventually screw things up as token information is pulled from cached headers. This is compounded by the fact that devise_token_auth doesn't send back any headers when in batch mode. One work around I tried from here is to attach some unique string to the request, for instance a timestamp. This prevents caching as every request is unique Vue.http.interceptors.push((request, next) => {
request.url += (request.url.indexOf('?') > 0 ? '&' : '?') + `cb=${new Date().getTime()}`
next()
}) Pretty hacky, but it does work. However, other users of devise_token_auth think the API ought to be sending back headers even when in batch mode, and just set them to blank or something to let the client know not to change its stored token. So I went with a fix that sets "Access-Token" to "". This will require vue-auth to make sure its not then trying to save a blank value. The current code is going to save something that looks like a bunch of response: function (res) {
var token = [],
headers = this.options._getHeaders.call(this, res);
if (headers['Access-Token']) {
this.options.deviseAuth.tokens.forEach(function (tokenName) {
if (headers[tokenName]) {
token.push(headers[tokenName]);
}
});
return token.join(';');
} else {
return false;
}
}
} Kind of annoying. But really just due to devise_token_auth doing some weird stuff. It looks like the caching thing just became an issue in the last couple of months. I imagine the gem will be fixed to not require any sort of work around on the client as it seems to me that normal browser behavior (caching XHR) shouldn't disrupt anything. |
Ok, I put that in for you. Another thing, I'm not sure about devise. But this sounds similar to an issue I had with Laravel JWT Auth library. It was basically invalidating tokens immediately instead of letting expired ones actually still be valid for a minute so that many async requests could still go through without sending a 401. There was a config setting to change this and all worked after that. Maybe something similar in devise? |
Yes devise token auth has some settings related to this that would probably fix things but imo would make things less secure. For instance I like the fact that the token is constantly being refreshed but this could be turned off and that would help here. Ultimately I'm not too sad about the workarounds and I imagine devise_token_auth will fix things up on their end before too long. Thank you the addition! |
v1.5.1-beta working great for me. The only devise thing I'm using is: authType: 'devise' Nice work! |
Note the new driver centric model in 2.x stream now (for anyone still watching the issue). Please check the updated docs and change log. |
Rob, this is fantastic. Thank you for pioneering this plugin. I've used it on a few APIs and I'm impressed.
Recently I found myself having to authenticate to a rails API using devise as their JWT provider, which seems common in the rails world. Well, the API requires 3 tokens to help facilitate simultaneous multi-user auth.
How difficult would it be to handle the config to accept an array of intercepted header tokens? Perhaps you could combine the token, tokenHeader, and authType into this format:
example config:
j-toker (a jquery based auth) handles their config like this
I understand I can overwrite the existing methods via config (thank you!) , but just thought I would offer some suggestions to bring added flexibility out-of-the-box.
Thanks for all your work!
The text was updated successfully, but these errors were encountered: