-
Notifications
You must be signed in to change notification settings - Fork 0
[#18] [API] As a user, I can register an account, [#17] [API] As a user, I can login #46
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Api | ||
| module V1 | ||
| class ApplicationController < ActionController::API | ||
| # equivalent of authenticate_user! on devise, but this one will check the oauth token | ||
| before_action :doorkeeper_authorize! | ||
|
|
||
| private | ||
|
|
||
| # helper method to access the current user from the token | ||
| def current_user | ||
| @current_user ||= User.find_by(id: doorkeeper_token[:resource_owner_id]) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,75 @@ | ||||
| # frozen_string_literal: true | ||||
|
|
||||
| module Api | ||||
| module V1 | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
| class UsersController < ApplicationController | ||||
| skip_before_action :doorkeeper_authorize!, only: %i[create] | ||||
|
|
||||
| def create | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
| user = build_user | ||||
| client_app = find_client_application | ||||
|
|
||||
| return render(json: { error: 'Invalid client ID' }, status: :forbidden) unless client_app | ||||
|
|
||||
| if user.save | ||||
| access_token = create_access_token(user, client_app) | ||||
| render_success_response(user, access_token) | ||||
| else | ||||
| render_failure_response(user) | ||||
| end | ||||
| end | ||||
|
|
||||
| private | ||||
|
|
||||
| def user_params | ||||
| params.permit(:email, :password) | ||||
| end | ||||
|
|
||||
| def build_user | ||||
| User.new(email: user_params[:email], password: user_params[:password]) | ||||
| end | ||||
|
|
||||
| def find_client_application | ||||
| Doorkeeper::Application.find_by(uid: params[:client_id]) | ||||
| end | ||||
|
|
||||
| def generate_refresh_token | ||||
| loop do | ||||
| token = SecureRandom.hex(32) | ||||
| break token unless Doorkeeper::AccessToken.exists?(refresh_token: token) | ||||
| end | ||||
| end | ||||
|
|
||||
| def create_access_token(user, client_app) | ||||
| Doorkeeper::AccessToken.create( | ||||
| resource_owner_id: user.id, | ||||
| application_id: client_app.id, | ||||
| refresh_token: generate_refresh_token, | ||||
| expires_in: Doorkeeper.configuration.access_token_expires_in.to_i, | ||||
| scopes: '' | ||||
| ) | ||||
| end | ||||
|
|
||||
| def render_success_response(user, access_token) | ||||
| user_data = build_user_data(user, access_token) | ||||
| render(json: { user: user_data }) | ||||
| end | ||||
|
|
||||
| def build_user_data(user, access_token) | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
| { | ||||
| id: user.id, | ||||
| email: user.email, | ||||
| access_token: access_token.token, | ||||
| token_type: 'bearer', | ||||
| expires_in: access_token.expires_in, | ||||
| refresh_token: access_token.refresh_token, | ||||
| created_at: access_token.created_at.to_time.to_i | ||||
| } | ||||
| end | ||||
|
|
||||
| def render_failure_response(user) | ||||
| render(json: { error: user.errors.full_messages }, status: :unprocessable_entity) | ||||
| end | ||||
| end | ||||
| end | ||||
| end | ||||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -7,5 +7,12 @@ module Authenticable | |||
| # Include default devise modules. Others available are: | ||||
| # :confirmable, :lockable, :timeoutable and :omniauthable | ||||
| devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable | ||||
|
|
||||
| # the authenticate method from devise documentation | ||||
| # todo extract it inside authenticable | ||||
| def self.authenticate(email, password) | ||||
| user = User.find_for_authentication(email: email) | ||||
| user&.valid_password?(password) ? user : nil | ||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||
| end | ||||
| end | ||||
| end | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| Doorkeeper.configure do | ||
| # Change the ORM that doorkeeper will use (requires ORM extensions installed). | ||
| # Check the list of supported ORMs here: https://github.com/doorkeeper-gem/doorkeeper#orms | ||
| orm :active_record | ||
|
|
||
| # This block will be called to check whether the resource owner is authenticated or not. | ||
| resource_owner_authenticator do | ||
| # Put your resource owner authentication logic here. | ||
| # Example implementation: | ||
| # User.find_by(id: session[:user_id]) || redirect_to(new_user_session_url) | ||
| end | ||
|
|
||
| resource_owner_from_credentials do |_routes| | ||
| User.authenticate(params[:email], params[:password]) | ||
| end | ||
|
|
||
| use_refresh_token | ||
| allow_blank_redirect_uri true | ||
| grant_flows %w[password] | ||
| skip_authorization do |resource_owner, client| | ||
| true | ||
| end | ||
|
|
||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| en: | ||
| activerecord: | ||
| attributes: | ||
| doorkeeper/application: | ||
| name: 'Name' | ||
| redirect_uri: 'Redirect URI' | ||
| errors: | ||
| models: | ||
| doorkeeper/application: | ||
| attributes: | ||
| redirect_uri: | ||
| fragment_present: 'cannot contain a fragment.' | ||
| invalid_uri: 'must be a valid URI.' | ||
| unspecified_scheme: 'must specify a scheme.' | ||
| relative_uri: 'must be an absolute URI.' | ||
| secured_uri: 'must be an HTTPS/SSL URI.' | ||
| forbidden_uri: 'is forbidden by the server.' | ||
| scopes: | ||
| not_match_configured: "doesn't match configured on the server." | ||
|
|
||
| doorkeeper: | ||
| applications: | ||
| confirmations: | ||
| destroy: 'Are you sure?' | ||
| buttons: | ||
| edit: 'Edit' | ||
| destroy: 'Destroy' | ||
| submit: 'Submit' | ||
| cancel: 'Cancel' | ||
| authorize: 'Authorize' | ||
| form: | ||
| error: 'Whoops! Check your form for possible errors' | ||
| help: | ||
| confidential: 'Application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.' | ||
| redirect_uri: 'Use one line per URI' | ||
| blank_redirect_uri: "Leave it blank if you configured your provider to use Client Credentials, Resource Owner Password Credentials or any other grant type that doesn't require redirect URI." | ||
| scopes: 'Separate scopes with spaces. Leave blank to use the default scopes.' | ||
| edit: | ||
| title: 'Edit application' | ||
| index: | ||
| title: 'Your applications' | ||
| new: 'New Application' | ||
| name: 'Name' | ||
| callback_url: 'Callback URL' | ||
| confidential: 'Confidential?' | ||
| actions: 'Actions' | ||
| confidentiality: | ||
| 'yes': 'Yes' | ||
| 'no': 'No' | ||
| new: | ||
| title: 'New Application' | ||
| show: | ||
| title: 'Application: %{name}' | ||
| application_id: 'UID' | ||
| secret: 'Secret' | ||
| secret_hashed: 'Secret hashed' | ||
| scopes: 'Scopes' | ||
| confidential: 'Confidential' | ||
| callback_urls: 'Callback urls' | ||
| actions: 'Actions' | ||
| not_defined: 'Not defined' | ||
|
|
||
| authorizations: | ||
| buttons: | ||
| authorize: 'Authorize' | ||
| deny: 'Deny' | ||
| error: | ||
| title: 'An error has occurred' | ||
| new: | ||
| title: 'Authorization required' | ||
| prompt: 'Authorize %{client_name} to use your account?' | ||
| able_to: 'This application will be able to' | ||
| show: | ||
| title: 'Authorization code' | ||
| form_post: | ||
| title: 'Submit this form' | ||
|
|
||
| authorized_applications: | ||
| confirmations: | ||
| revoke: 'Are you sure?' | ||
| buttons: | ||
| revoke: 'Revoke' | ||
| index: | ||
| title: 'Your authorized applications' | ||
| application: 'Application' | ||
| created_at: 'Created At' | ||
| date_format: '%Y-%m-%d %H:%M:%S' | ||
|
|
||
| pre_authorization: | ||
| status: 'Pre-authorization' | ||
|
|
||
| errors: | ||
| messages: | ||
| # Common error messages | ||
| invalid_request: | ||
| unknown: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' | ||
| missing_param: 'Missing required parameter: %{value}.' | ||
| request_not_authorized: 'Request need to be authorized. Required parameter for authorizing request is missing or invalid.' | ||
| invalid_redirect_uri: "The requested redirect uri is malformed or doesn't match client redirect URI." | ||
| unauthorized_client: 'The client is not authorized to perform this request using this method.' | ||
| access_denied: 'The resource owner or authorization server denied the request.' | ||
| invalid_scope: 'The requested scope is invalid, unknown, or malformed.' | ||
| invalid_code_challenge_method: 'The code challenge method must be plain or S256.' | ||
| server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' | ||
| temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.' | ||
|
|
||
| # Configuration error messages | ||
| credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.' | ||
| resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfigured.' | ||
| admin_authenticator_not_configured: 'Access to admin panel is forbidden due to Doorkeeper.configure.admin_authenticator being unconfigured.' | ||
|
|
||
| # Access grant errors | ||
| unsupported_response_type: 'The authorization server does not support this response type.' | ||
| unsupported_response_mode: 'The authorization server does not support this response mode.' | ||
|
|
||
| # Access token errors | ||
| invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.' | ||
| invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.' | ||
| unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.' | ||
|
|
||
| invalid_token: | ||
| revoked: "The access token was revoked" | ||
| expired: "The access token expired" | ||
| unknown: "The access token is invalid" | ||
| revoke: | ||
| unauthorized: "You are not authorized to revoke this token" | ||
|
|
||
| forbidden_token: | ||
| missing_scope: 'Access to this resource requires scope "%{oauth_scopes}".' | ||
|
|
||
| flash: | ||
| applications: | ||
| create: | ||
| notice: 'Application created.' | ||
| destroy: | ||
| notice: 'Application deleted.' | ||
| update: | ||
| notice: 'Application updated.' | ||
| authorized_applications: | ||
| destroy: | ||
| notice: 'Application revoked.' | ||
|
|
||
| layouts: | ||
| admin: | ||
| title: 'Doorkeeper' | ||
| nav: | ||
| oauth2_provider: 'OAuth2 Provider' | ||
| applications: 'Applications' | ||
| home: 'Home' | ||
| application: | ||
| title: 'OAuth authorization required' |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.