class OauthController < ApplicationController class ApiOAuthError < StandardError attr_accessor :code, :description, :uri, :state def initialize(code, description, uri = nil, state = nil) @code = code @description = description @uri = uri @state = state end end before_filter :authenticate_user!, :except => [:access_token] before_filter :expires_now skip_before_filter :app_initialization, :retrieve_api, :verify_authenticity_token, :only => [:access_token] rescue_from ApiOAuthError, :with => :oauth2_error def oauth2_error(e) # see http://tools.ietf.org/html/draft-ietf-oauth-v2-10 error = { :error => e.code } error[:error_description] = e.description unless e.description.blank? error[:error_uri] = e.uri unless e.uri.blank? error[:state] = e.state unless e.state.blank? render :json => error, :status => 401 end def authorize # client id client_id = params[:client_id] raise ApiOAuthError.new(:invalid_client, "missing client_id", nil, params[:state]) if client_id.blank? # response type response_type = params[:response_type] raise ApiOAuthError.new(:unsupported_response_type, "missing response_type", nil, params[:state]) if response_type.blank? raise ApiOAuthError.new(:unsupported_response_type, "unsupported response_type: #{params[:response_type]}", nil, params[:state]) unless response_type == 'code' # client application application = ClientApplication.where({:app_id => client_id}).first raise ApiOAuthError.new(:invalid_client, "invalid client_id: #{client_id}", nil, params[:state]) unless application raise ApiOAuthError.new(:access_denied, "invalid client_id: #{client_id}", nil, params[:state]) unless application.access_granted # TODO: raise unauthorized_client if client is disabled AccessGrant.prune! access_grant = current_user.access_grants.create({:application => application}) redirect_uri = access_grant.redirect_uri_for(params[:redirect_uri], params[:state]) redirect_to redirect_uri end def access_token # client id client_id = params[:client_id] raise ApiOAuthError.new(:invalid_client, "missing client_id", nil, params[:state]) if client_id.blank? # client application application = ClientApplication.where({:app_id => client_id}).first raise ApiOAuthError.new(:invalid_client, "invalid client_id: #{client_id}", nil, params[:state]) unless application # TODO: raise unauthorized_client if client is disabled application = ClientApplication.authenticate(params[:client_id], params[:client_secret]) raise ApiOAuthError.new(:access_denied, "invalid client_secret: #{params[:client_secret]}", nil, params[:state]) unless application # grant type grant_type = params[:grant_type] case grant_type when "authorization_code" then code = params[:code] raise ApiOAuthError.new(:invalid_grant, "missing code", nil, params[:state]) if code.blank? access_grant = AccessGrant.authenticate(code, application.id) raise ApiOAuthError.new(:invalid_grant, "invalid code: #{params[:code]}", nil, params[:state]) unless access_grant when "credentials" then AccessGrant.prune! email = params[:email] raise ApiOAuthError.new(:invalid_grant, "missing e-mail", nil, params[:state]) if email.blank? password = params[:password] raise ApiOAuthError.new(:invalid_grant, "missing password", nil, params[:state]) if password.blank? user = User.where({email: email}).first raise ApiOAuthError.new(:invalid_grant, "invalid e-mail or password", nil, params[:state]) if user.nil? or ! user.valid_password?(password) access_grant = user.access_grants.create({:application => application}) else raise ApiOAuthError.new(:unsupported_grant_type, "unsupported grant type: #{params[:grant_type]}", nil, params[:state]) end access_grant.start_expiry_period! token = { :access_token => access_grant.access_token, # :refresh_token => access_grant.refresh_token, :expires_in => access_grant.access_token_expires_at } token[:state] = params[:state] unless params[:state].blank? render :json => token end end