mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-08 15:05:22 +02:00
Fix OAuth mobile app support with custom URL schemes
- Configure Doorkeeper to allow custom URL schemes (maybeapp://)
- Disable force_ssl_in_redirect_uri to support non-HTTPS schemes
- Add custom Doorkeeper views with mobile OAuth detection
- Disable Turbo for mobile OAuth flows to prevent redirect interference
- Add display parameter preservation through OAuth flow
- Create custom Doorkeeper layouts with proper styling
- Add comprehensive integration tests for mobile OAuth flows
- Ensure all OAuth pages use proper doorkeeper/application layout
This allows the mobile app to complete OAuth authorization flows
without the web app interfering with custom URL scheme redirects.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
404066eaa1
commit
cba0bdf0e2
17 changed files with 513 additions and 4 deletions
6
app/views/doorkeeper/applications/_delete_form.html.erb
Normal file
6
app/views/doorkeeper/applications/_delete_form.html.erb
Normal file
|
@ -0,0 +1,6 @@
|
|||
<%- submit_btn_css ||= 'btn btn-link' %>
|
||||
<%= form_tag oauth_application_path(application), method: :delete do %>
|
||||
<%= submit_tag t('doorkeeper.applications.buttons.destroy'),
|
||||
onclick: "return confirm('#{ t('doorkeeper.applications.confirmations.destroy') }')",
|
||||
class: submit_btn_css %>
|
||||
<% end %>
|
59
app/views/doorkeeper/applications/_form.html.erb
Normal file
59
app/views/doorkeeper/applications/_form.html.erb
Normal file
|
@ -0,0 +1,59 @@
|
|||
<%= form_for application, url: doorkeeper_submit_path(application), as: :doorkeeper_application, html: { role: 'form' } do |f| %>
|
||||
<% if application.errors.any? %>
|
||||
<div class="alert alert-danger" data-alert><p><%= t('doorkeeper.applications.form.error') %></p></div>
|
||||
<% end %>
|
||||
|
||||
<div class="form-group row">
|
||||
<%= f.label :name, class: 'col-sm-2 col-form-label font-weight-bold' %>
|
||||
<div class="col-sm-10">
|
||||
<%= f.text_field :name, class: "form-control #{ 'is-invalid' if application.errors[:name].present? }", required: true %>
|
||||
<%= doorkeeper_errors_for application, :name %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<%= f.label :redirect_uri, class: 'col-sm-2 col-form-label font-weight-bold' %>
|
||||
<div class="col-sm-10">
|
||||
<%= f.text_area :redirect_uri, class: "form-control #{ 'is-invalid' if application.errors[:redirect_uri].present? }" %>
|
||||
<%= doorkeeper_errors_for application, :redirect_uri %>
|
||||
<span class="form-text text-secondary">
|
||||
<%= t('doorkeeper.applications.help.redirect_uri') %>
|
||||
</span>
|
||||
|
||||
<% if Doorkeeper.configuration.allow_blank_redirect_uri?(application) %>
|
||||
<span class="form-text text-secondary">
|
||||
<%= t('doorkeeper.applications.help.blank_redirect_uri') %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<%= f.label :confidential, class: 'col-sm-2 form-check-label font-weight-bold' %>
|
||||
<div class="col-sm-10">
|
||||
<%= f.check_box :confidential, class: "checkbox #{ 'is-invalid' if application.errors[:confidential].present? }" %>
|
||||
<%= doorkeeper_errors_for application, :confidential %>
|
||||
<span class="form-text text-secondary">
|
||||
<%= t('doorkeeper.applications.help.confidential') %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<%= f.label :scopes, class: 'col-sm-2 col-form-label font-weight-bold' %>
|
||||
<div class="col-sm-10">
|
||||
<%= f.text_field :scopes, class: "form-control #{ 'has-error' if application.errors[:scopes].present? }" %>
|
||||
<%= doorkeeper_errors_for application, :scopes %>
|
||||
<span class="form-text text-secondary">
|
||||
<%= t('doorkeeper.applications.help.scopes') %>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<%= f.submit t('doorkeeper.applications.buttons.submit'), class: 'btn btn-primary' %>
|
||||
<%= link_to t('doorkeeper.applications.buttons.cancel'), oauth_applications_path, class: 'btn btn-secondary' %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
5
app/views/doorkeeper/applications/edit.html.erb
Normal file
5
app/views/doorkeeper/applications/edit.html.erb
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div class="border-bottom mb-4">
|
||||
<h1><%= t('.title') %></h1>
|
||||
</div>
|
||||
|
||||
<%= render 'form', application: @application %>
|
38
app/views/doorkeeper/applications/index.html.erb
Normal file
38
app/views/doorkeeper/applications/index.html.erb
Normal file
|
@ -0,0 +1,38 @@
|
|||
<div class="border-bottom mb-4">
|
||||
<h1><%= t('.title') %></h1>
|
||||
</div>
|
||||
|
||||
<p><%= link_to t('.new'), new_oauth_application_path, class: 'btn btn-success' %></p>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= t('.name') %></th>
|
||||
<th><%= t('.callback_url') %></th>
|
||||
<th><%= t('.confidential') %></th>
|
||||
<th><%= t('.actions') %></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @applications.each do |application| %>
|
||||
<tr id="application_<%= application.id %>">
|
||||
<td class="align-middle">
|
||||
<%= link_to application.name, oauth_application_path(application) %>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<%= simple_format(application.redirect_uri) %>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<%= application.confidential? ? t('doorkeeper.applications.index.confidentiality.yes') : t('doorkeeper.applications.index.confidentiality.no') %>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(application), class: 'btn btn-link' %>
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
<%= render 'delete_form', application: application %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
5
app/views/doorkeeper/applications/new.html.erb
Normal file
5
app/views/doorkeeper/applications/new.html.erb
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div class="border-bottom mb-4">
|
||||
<h1><%= t('.title') %></h1>
|
||||
</div>
|
||||
|
||||
<%= render 'form', application: @application %>
|
63
app/views/doorkeeper/applications/show.html.erb
Normal file
63
app/views/doorkeeper/applications/show.html.erb
Normal file
|
@ -0,0 +1,63 @@
|
|||
<div class="border-bottom mb-4">
|
||||
<h1><%= t('.title', name: @application.name) %></h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h4><%= t('.application_id') %>:</h4>
|
||||
<p><code class="bg-light" id="application_id"><%= @application.uid %></code></p>
|
||||
|
||||
<h4><%= t('.secret') %>:</h4>
|
||||
<p>
|
||||
<code class="bg-light" id="secret">
|
||||
<% secret = flash[:application_secret].presence || @application.plaintext_secret %>
|
||||
<% if secret.blank? && Doorkeeper.config.application_secret_hashed? %>
|
||||
<span class="bg-light font-italic text-uppercase text-muted"><%= t('.secret_hashed') %></span>
|
||||
<% else %>
|
||||
<%= secret %>
|
||||
<% end %>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<h4><%= t('.scopes') %>:</h4>
|
||||
<p>
|
||||
<code class="bg-light" id="scopes">
|
||||
<% if @application.scopes.present? %>
|
||||
<%= @application.scopes %>
|
||||
<% else %>
|
||||
<span class="bg-light font-italic text-uppercase text-muted"><%= t('.not_defined') %></span>
|
||||
<% end %>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<h4><%= t('.confidential') %>:</h4>
|
||||
<p><code class="bg-light" id="confidential"><%= @application.confidential? %></code></p>
|
||||
|
||||
<h4><%= t('.callback_urls') %>:</h4>
|
||||
|
||||
<% if @application.redirect_uri.present? %>
|
||||
<table>
|
||||
<% @application.redirect_uri.split.each do |uri| %>
|
||||
<tr>
|
||||
<td>
|
||||
<code class="bg-light"><%= uri %></code>
|
||||
</td>
|
||||
<td>
|
||||
<%= link_to t('doorkeeper.applications.buttons.authorize'), oauth_authorization_path(client_id: @application.uid, redirect_uri: uri, response_type: 'code', scope: @application.scopes), class: 'btn btn-success', target: '_blank' %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
<% else %>
|
||||
<span class="bg-light font-italic text-uppercase text-muted"><%= t('.not_defined') %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<h3><%= t('.actions') %></h3>
|
||||
|
||||
<p><%= link_to t('doorkeeper.applications.buttons.edit'), edit_oauth_application_path(@application), class: 'btn btn-primary' %></p>
|
||||
|
||||
<p><%= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger' %></p>
|
||||
</div>
|
||||
</div>
|
Loading…
Add table
Add a link
Reference in a new issue