Open epicmonkey opened 9 years ago
this how I implemented refresh token using client
def refresh
status = false
if (uid = params['Uid']).present? &&
(client = params['Client']).present?
if (account = Account.find_by_uid(uid)).present?
if account.tokens[client].present?
response.headers.merge!(account.create_new_auth_token(client))
status = true
end
end
end
if status
render json: {data: account.token_validation_response}
else
head :unauthorized
end
end
@booleanbetrayal can we have something similar to this
I also very much need refresh_token! I'm trying to implement my google_oauth2 strategy with google-api-client gem, but I need a refresh_token, and it seems like devise_token_auth is getting in the way at the point of mount_devise_token_auth_for ...
I have, for example,
mount_devise_token_auth_for 'User', at: 'auth', :controllers => { :omniauth_callbacks => "users/omniauth_callbacks" }
And then:
class Users::OmniauthCallbacksController < DeviseTokenAuth::OmniauthCallbacksController def assign_provider_attributes(user, auth_hash) user.assign_attributes({ name: auth_hash['info']['name'], image: auth_hash['info']['image'], email: auth_hash['info']['email'], refresh_token: auth_hash['credentials']['refresh_token'] }) end end
But this does not save the refresh_token to the DB. Any help?
I replaced the above assign_provider_attributes with code from the dummy app for "custom/omniauth_callbacks", but I get nil when trying to print the resource.
def omniauth_success super do |resource| pp resource # this returns nil @omniauth_success_block_called = true end end
def omniauth_success_block_called? @omniauth_success_block_called == true end
And when I try to pp resource in omniauth_success, I get nil in the console. I guess I don't understand the ordering of the processes that are going on. Shouldn't resource equate to something for me to use?
I see my mistake after looking through DeviseTokenAuth::OmniauthCallbacksController...
I just needed to be using @resource, and I can access auth_hash['credentials'] as well.
Here's a refresh token implementation as a separate controller, in case someone is interested:
controller class:
class TokenRefreshesController < DeviseTokenAuth::ApplicationController
before_action :set_user_by_token, only: [:refresh_token]
def refresh_token
if @resource
auth_header = @resource.create_new_auth_token(@client_id)
response.headers.merge!(auth_header)
render json: {
data: resource_data(resource_json: @resource.token_validation_response)
}
else
head :unauthorized
end
end
end
routes.rb:
get 'refresh_token', to: 'token_refreshes#refresh_token'
Hey @wangthony
I tried your code for refresh token, but I'm getting unauthorized error.
Is it because I'm trying to refresh the auth token after the access-token has expired?
Also had some problem with routes first, but placing the route definition inside devise_scope :user block did the trick.
The headers I'm sending from my client application: client: 'xxxxx' access-token: 'xxxxxx' uid: 'xxxxx' 'token-type': 'Bearer' 'Accept': 'application/json'
Any help much appreciated, Thanks
Is there any improvements?
There is no such thing as a refresh token in devise token auth. However there is a secure mechanism that changes the token after every request. https://devise-token-auth.gitbook.io/devise-token-auth/security
In order to do that, you need to set change_headers_on_each_request
to true. See https://devise-token-auth.gitbook.io/devise-token-auth/config/initialization#about-token-management
On your frontend, you will have to make sure that you replace your current token with the new one (if more recent). If you are using ember-simple-auth, let me know if you need assistance.
@stephanebruckert Thank you but i need refresh token. I can't force my mobile users to logout and login again. It has to be persistent session. Users can close the application without waiting for a response from server. And if the app closes, I can not save token on the phone. What am I supposed to do in this situation?
@ccoeder can you explain this into more details please I don't understand.
if the app closes, I can not save token on the phone
I don't see why you can't save a token on the phone? Also wouldn't it be the same issue with a refresh token?
@stephanebruckert sorry for my bad english. Im using axios interceptors on react-native. see interceptors
instance.interceptors.response.use(
async (response) => {
if (response.headers['access-token']) {
await onSignIn(response.headers);
}
return response;
},
error => Promise.reject(error),
);
If headers has access-token im saving it. But there is an issue with this solution. If the user closes the application before the interceptor is running, the code here does not work. The token on the server side has changed. But the client sends a request with the old token.
Ha I see, basically send a request and immediately close the app before the response comes? Indeed that's an interesting case.
Did you try it or are you just assuming that it is going to cause an issue? We have been using this mechanism for months now in our backend but never had this issue on our iOS app.
I will try to reproduce it on my side and will let you know.
Yes, some of my customers told me that they can't see anything in their screens. So i investigated and found they are getting 401 from server.
Thanks for answers by the way.
@hcyildirim Did you ever find a solution to this issue? I tried to set-up my own solution just using JWT and refresh tokens, which worked quite well, but I found it difficult to integrate social login which led me back to devise_token_auth
@MaicolBen would the core team approve of this as an added feature to the gem? This would basically allow developers to choose between 2 different implementation styles:
change_headers_on_each_request = true
: the token refreshes on every request. This has know performance issues (#922) and flow issues (new tokens not returned on some errors), but it's quite secure!change_headers_on_each_request = false
: this is less secure, but we can set the token_lifetime
to a reasonable value to make it so. And with the addition of a "/auth/refresh_token" endpoint, the token can be refreshed before it expires.Yes, that doesn't seem like a braking change. Unfortunately, we need someone to submit a pull request for this feature, but I'm happy to review the code and approve it
Here is an updated version considering refresh token only if the last token was provided in headers
def refresh_token
status = false
if (uid = request.headers['uid']).present? && (client = request.headers['client']).present?
if (user = User.find_by(uid: uid)).present?
if user.tokens[client].present? && DeviseTokenAuth::Concerns::User.tokens_match?(user.tokens[client]['last_token'], request.headers['access-token'])
headers = user.create_new_auth_token(client)
response.set_header('access-token', headers['access-token'])
response.set_header('expiry', headers['expiry'])
status = true
end
end
end
if status
render json: {data: user.token_validation_response}
else
head :unauthorized
end
end
@heberuriegas
Thanks for your snippet!
I guess we should revoke last_token
once a user sign out, to ensure that the token of signed out user would not be used on refreshing access token (if revoking a token is difficult, create a new one, which we do not send to client, for that purpose).
I didn't find this in docs, is there
refresh_token
support?