Authenticate active admin user in grape api endpoint - ruby-on-rails

So here are the components of my application
admin panel is on myapp.com/admin built with active admin. then I have a dashboard for users at dashboard.myapp.com built with AngularJs and then I have api built with grape at myapp.com/api.
I have a feature on my admin panel where an admin can log in on the dashboard as another user. we call this capture_session. basically the users session is captured by admin and we drop a cookie admin_capture whose value is the id of the admin_user.
now here is what I want to achieve. in one of my grape api endpoints I need validate that the session is captured and that the admin user who has captured the session is also logged in on the admin panel.
now figuring out whether the session is captured or not is easy as I already have a cookie for that. but how can I verify that the admin user is logged in?
as active admin methods and helpers cannot be called in grape api endpoint.
can I achieve this with cookies or sessions? any help will be highly appreciated
Thanks

So here is how I solved this..
I set another cookie admin_session with the encrypted session and then returned the same cookie as header from the dashboard.
In the grape endpoint I decrypted the session and got the admin user's id and initial part of the encrypted password. and used this information to verify admin.
here is the code.
admin_id = ADMIN_ID_HEADER
admin_session = ADMIN_SESSION_HEADER
admin_user = AdminUser.find_by_id(admin_id)
return unless admin_id.present? && admin_session.present? && admin_user.present?
salt = Rails.application.config.action_dispatch.encrypted_cookie_salt
signed_salt = Rails.application.config.action_dispatch.encrypted_signed_cookie_salt
key_generator = ActiveSupport::KeyGenerator.new(ENV['SECRET_KEY_BASE'], :iterations => 1000)
secret = key_generator.generate_key(salt)
signed_secret = key_generator.generate_key(signed_salt)
encryptor = ActiveSupport::MessageEncryptor.new(secret, signed_secret, :serializer => ActiveSupport::MessageEncryptor::NullSerializer)
session_data = JSON.parse(encryptor.decrypt_and_verify(CGI::unescape(admin_session))) rescue {}

Related

Authenticating docusign via Rails API (Omniauth + Devise) + JS Frontend

I'm trying to create an authentication flow using Auth Code Grant where I've added necessary omniauth strategy for Docusign to create /auth/docusign routes in Rails API only application.
Here are the steps followed
I'm issuing a request to the route from VueJS client.
window.open("http://localhost:4000/auth/docusign", "targetWindow", "width=350,height=250")
After user enters credentials and on successful login I'm calling the callback:
class SessionsController < Devise::SessionsController
def docusign
internal_destroy
#success = false
userinfo = request.env['omniauth.auth']
request_info = request.env['omniauth.params']
if userinfo
info = userinfo.info
cred = userinfo.credentials
user = User.find_by(email: info['email']) || User.find_by(id: session[:user_id])
if user
organization = user.organization
organization.organization_providers.where(provider_name: 'Docusign').destroy_all
OrganizationProvider.create(email: info['email'], token_expires_at: Time.at(cred['expires_at']), token_expires_at: Time.now, provider_name: 'Docusign', organization_id: organization.id, token: cred.token)
#success = true
end
end
render 'sessions/docusign'
end
end
I'd like to pass some params (which I'm accessing in the callback as request.env['omniauth.params']) for executing some backend tasks in the method.
When I try window.open("http://localhost:4000/auth/docusign?email='"+email+"'", "targetWindow", "width=350,height=250")
It says that the url doesn't match with any redirect urls
I have also tried passing in redirect_to('/auth/docusign', query: query) but on doing so, it doesn't open in a browser due to CORS.
I'm also trying to set it in session cookie, but since it's an API only server, I'm still working towards setting up cookie store.
Question
Which is the best way to achieve this? To pass some params in the callback and retrieve it.
Then the execution flow continues on the Rails server and the window serves a page with an appropriate response as per authentication status. However during this time, the client window which started the request is not aware of the authentication outcome.
Question
How can I communicate to the VueJS client that the authentication process is completed?
Question
Am I doing the above flow correctly or are there any better ways to achieve the same?
Thanks in advance
You need to log into your DocuSign Developer Account, Click on Admin and go on the left nav down to "API and Keys" where you can find the integration key you set. Did you set one?
If you did, you should find it and then add the redirectUri to the OAuth settings for that key (client ID in OAuth).
That is why DocuSign login tells you that the redirectURI doesn't match. You can add http://localhost:4000/auth to the list and that should work for your local env.
You cannot past custom variables on the redirectUri, it has to match exactly to the one you entered. If you need to pass values to it, there's a way to do that using state.
Here is how the URL should look, notice the &state= part of it:
https://account-d.docusign.com/oauth/auth?
response_type=code
&scope=YOUR_REQUESTED_SCOPES
&client_id=YOUR_INTEGRATION_KEY
&state=YOUR_CUSTOM_STATE
&redirect_uri=YOUR_REDIRECT_URI
&login_hint=YOUR_LOGIN_HINT
You can put whatever you want in there (URI encoded of course) and that value would come back to you when redirected back also with &state= parameter.
This solves the problem and allows you to pass arguments back to your redirect URI.

Devise token auth another user login

I am developing an application through Rails.
I need the ability to login to another user with admin account in devise token auth gem
When user log in, authHeaders are stored in the browser cookie.
Determine user by authHeaders value.
ex)
{ %22access-token%22:%22"access-token value"%22%2C
%22token-type%22:%22Bearer%22%2C
%22client%22:%22"client value"%22%2C
%22expiry%22:%"expiry value"%22%2C
%22uid%22:%22"uid value"%22}
in my controller
returns a new token value when admin clicks user id
def login_as
user = User.find(params[:user_id])
user_new_token_data = user.create_new_auth_token
## return new access-token, token-type, client, expiry, uid in browser cookies format same value
end
I want to change the return value to a browser cookie value and log in as another user, but I do not know how to put it in the cookie.
I would appreciate your help. Thank you

How authenticate a user in active directory that have to change his password?

I'm trying to login an account in Active Directory with have to change this password (pwdlastset = '0'). The problem I've found is that when this propertie of the entity is enabled, the ldap method can not authenticate with old password. I'am doing the connection with a ruby client and I need to authenticate the user before send the replace instruction to de unicodePwd to avoid security problems. How can I check old password when I have pwdlastset enabled?
A bit of pseudocode:
ldap_con.new(ldap_params)
auth = ldap_con.auth(login, old_pass) # Fail when pwdlastset eql 0
ldap_con.modify(:dn => dn, :operations => [:replace, "unicodePwd", new_pass]) if auth
If I change the pass without authenticate, someone can change the pass of an account without old credentials.
Thanks!
You can set the pwdLastSet to "-1" and then set the password for the user.
As I recall, these need to be done in two LDAP operations.
-jim

Devise login custom endpoint, current_user session id

I have a mobile application that needs to login to a legacy Rails 3.2 app.
I have an endpoint that the mobile app makes a POST request to passing the username and password.
I want to verify the password and sign in the user and then need to save the now signed user's session id.
I have tried the following:
user = User.find_by_username(params[:username])
if user.valid_password?(params[:password])
sign_in(:user, user)
session_id = request.session_options[:id]
p "session id: #{session_id}"
current_user.logins.create({ session_id: session_id })
end
When I check my redis store that session id being stored is different from the session_id I print out. I read somewhere that this is because rails creates a session id for the request and then once you sign in you get a new session id to prevent fake requests. How can I get the signed in user's session_id?

Rails API: Authenticate users from native mobile apps using username/password or facebook token

So I have been pulling my hair out for a few days now trying to figure out how to add username/password authentication to my rails mobile API.
Here is a brief overview of my current authentication flow:
User selects "login with Facebook" on the mobile client, client
redirects to Facebook app and requests access_token
On success, Facebook responds with the access token and the client
redirects back to my app.
The client sends the access token to my API
My API uses the koala gem to check if the access token is valid.
If the token is valid, Facebook sends the users data to the API
where a new user is created. If the user already exists, my API sends down the users data.
My API handles the access token in step 4 as shown below:
def self.authenticate_user_from_facebook(fb_access_token)
user = User.new
graph = Koala::Facebook::API.new(fb_access_token)
profile = graph.get_object('me')
#user['fb_id'] = profile['id']
#user['fb_token'] = fb_access_token
# Generate user hash
uhash = Hash.new
uhash['provider'] = 'facebook'
uhash['uid'] = profile['id']
uhash['info'] = Hash.new
uhash['info']['nickname'] = profile['username']
uhash['info']['name'] = profile['name']
uhash['info']['email'] = profile['email']
uhash['info']['first_name'] = profile['first_name']
uhash['info']['last_name'] = profile['last_name']
uhash['info']['verified'] = profile['verified']
uhash['info']['urls'] = Hash.new
uhash['info']['urls']['Facebook'] = profile['link']
uhash['credentials'] = Hash.new
uhash['credentials']['token'] = fb_access_token
uhash['extra'] = Hash.new
uhash['extra']['raw_info'] = Hash.new
#Save the new data
user = User.apply_auth(uhash)
return user
end
def self.apply_auth(uhash)
User.where(:uid => uhash['uid'], :provider => uhash['provider']).first_or_create do |user|
user.provider = uhash['provider']
user.uid = uhash['uid']
user.nickname = uhash['info']['nickname']
user.email = uhash['info']['email']
user.name = uhash['info']['name']
user.first_name = uhash['info']['first_name']
user.last_name = uhash['info']['last_name']
end
end
Once the user is created, they can make requests to my API using their access token as shown below:
In step 2 the API is using koala to verify the users access token. This is done by applying the following before_filter to all controllers.
before_filter :current_user
and in my application_helper.rb
def current_user
#current_user ||= User.authenticate_user_from_facebook(params[:access_token])
end
Every time a user makes a request to my API the koala gem is used to check if the token is valid, then the request is processed.
What I am trying to add now is authentication with only username and password.
Things I have looked into
I have been constantly referring to Railscast 235, 209, 250, 82 and reading up on OAuth2. I have a basic understanding of how authentication works but Im having trouble applying it to my current authentication flow.
Devise Token Authentication
Referring to Railscast 235, 209, and this blog post:
http://matteomelani.wordpress.com/2011/10/17/authentication-for-mobile-devices/
I can understand how to login and validate a user who logs in with a username and password. However I am confused as to how that will mesh with my Facebook login flow. I do not understand how to create a session with devise for a user who already has an access token generated by Facebook.
OAuth2
Making my API an OAuth2 provider seems like it would be a good way to go, but it seems kind of silly to redirect to a browser, and I don't know if its possible to redirect back from the browser to my app.
Authentication From Scratch
This is the option I am thinking of going with but I would be reinventing the wheel.
Thanks for reading this long post! Any advice is appreciated!
You may want to look into Warden. Warden makes it easy to setup and use different auth strategies whether you use tokens, password or Facebook. It is Rack-based so it also works outside of Rails which is nice if you ever want to use something like Grape for the API.
Here is the RailsCast on Warden. (Pro subscription required)

Resources