Skip to content
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

Invalid redirect URI #7

Closed
mathieue opened this issue Nov 3, 2015 · 24 comments
Closed

Invalid redirect URI #7

mathieue opened this issue Nov 3, 2015 · 24 comments

Comments

@mathieue
Copy link

mathieue commented Nov 3, 2015

Can't get over it. I used 0.0.8 or 0.0.9. I can't debug.

This is the yaml of the call:

https://accounts.spotify.com/api/token

:raise_errors: true
:parse:
:body:
grant_type: authorization_code
code: AQCvXf4SDk2HiltAatn18IagKOC871rSk18E7vm-Lp9rHATXPXhfxEqAy7Yl50MQp3dypdWAA1iY1rc01glsouYhxuFAcH7TBGEMlK6EJMwCfxn7Ldz-3rn5b1XNWyXEYyESXiNS6O9rpbtdZSYr0WCywgBTva_Owv-3Og1CaNdXdef9hDzdNPhYHfL7zR61dKUJDSXkbeiK4cOjZIe9hKGcNnMW1QSWUhKE
client_id: xxx
client_secret: xxxx
:redirect_uri: http://backupmyplaylistsdev.net/auth/spotify/callback?code=AQCvXf4SDk2HiltAatn18IagKOC871rSk18E7vm-Lp9rHATXPXhfxEqAy7Yl50MQp3dypdWAA1iY1rc01glsouYhxuFAcH7TBGEMlK6EJMwCfxn7Ldz-3rn5b1XNWyXEYyESXiNS6O9rpbtdZSYr0WCywgBTva_Owv-3Og1CaNdXdef9hDzdNPhYHfL7zR61dKUJDSXkbeiK4cOjZIe9hKGcNnMW1QSWUhKE&state=10715cde9e932fd315f9d4da58e78b39a67c3384c1585c3b
:headers:

invalid_grant: Invalid redirect URI {"error":"invalid_grant","error_description":"Invalid redirect URI"}

@masterkain
Copy link
Member

ensure you have setup your redirect uri correctly in your spotify app preferences:
http://backupmyplaylistsdev.net/auth/spotify/callback (maybe http/https issue?)

also you might want to conceal the client_id and secret in the ticket.

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

Yes this callback is added:
http://backupmyplaylistsdev.net/auth/spotify/callback

For http/https i don't use https on the project.

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

On the same app, omniauth-deezer is working.

@masterkain
Copy link
Member

can you try a rake routes | grep spotify if rails app and paste the output?

also can you screenshot the spotify section about redirect uris? like this:

screen shot 2015-11-03 at 12 54 21

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

Mathieus-MacBook-Pro-2:/workspace/business-projects/backup-my-playlists$ rake routes | grep spotify
Mathieus-MacBook-Pro-2:
/workspace/business-projects/backup-my-playlists$

empty !

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

screen shot 2015-11-03 at 11 56 26

@masterkain
Copy link
Member

are you using devise or pure omniauth?

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

pure omniauth

@masterkain
Copy link
Member

can you try deleting the first and the third redirect uris and try again?

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

Mathieus-MacBook-Pro-2:/workspace/business-projects/backup-my-playlists$ rake routes | grep callback
GET /auth/:provider/callback(.:format) welcome#index
Mathieus-MacBook-Pro-2:
/workspace/business-projects/backup-my-playlists$

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

the same:

OAuth2::Error
invalid_grant: Invalid redirect URI {"error":"invalid_grant","error_description":"Invalid redirect URI"}

Extracted source (around line #117):
115
116
117
118
119
120

  when 400..599
    error = Error.new(response)
    fail(error) if opts.fetch(:raise_errors, options[:raise_errors])
    response.error = error
    response
  else

Rails.root: /Users/me/workspace/business-projects/backup-my-playlists

Application Trace | Framework Trace | Full Trace
oauth2 (1.0.0) lib/oauth2/client.rb:117:in request' oauth2 (1.0.0) lib/oauth2/client.rb:142:inget_token'
oauth2 (1.0.0) lib/oauth2/strategy/auth_code.rb:29:in get_token' omniauth-oauth2 (1.4.0) lib/omniauth/strategies/oauth2.rb:89:inbuild_access_token'
omniauth-oauth2 (1.4.0) lib/omniauth/strategies/oauth2.rb:73:in callback_phase' omniauth (1.2.2) lib/omniauth/strategy.rb:227:incallback_call'
omniauth (1.2.2) lib/omniauth/strategy.rb:184:in call!' omniauth (1.2.2) lib/omniauth/strategy.rb:164:incall'
omniauth (1.2.2) lib/omniauth/builder.rb:59:in call' rack (1.6.4) lib/rack/etag.rb:24:incall'
rack (1.6.4) lib/rack/conditionalget.rb:25:in call' rack (1.6.4) lib/rack/head.rb:13:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/params_parser.rb:27:in call' actionpack (4.2.4) lib/action_dispatch/middleware/flash.rb:260:incall'
rack (1.6.4) lib/rack/session/abstract/id.rb:225:in context' rack (1.6.4) lib/rack/session/abstract/id.rb:220:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/cookies.rb:560:in call' activerecord (4.2.4) lib/active_record/query_cache.rb:36:incall'
activerecord (4.2.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:653:in call' activerecord (4.2.4) lib/active_record/migration.rb:377:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/callbacks.rb:29:in block in call' activesupport (4.2.4) lib/active_support/callbacks.rb:88:inrun_callbacks'
activesupport (4.2.4) lib/active_support/callbacks.rb:778:in _run_call_callbacks' activesupport (4.2.4) lib/active_support/callbacks.rb:81:inrun_callbacks'
actionpack (4.2.4) lib/action_dispatch/middleware/callbacks.rb:27:in call' actionpack (4.2.4) lib/action_dispatch/middleware/reloader.rb:73:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/remote_ip.rb:78:in call' actionpack (4.2.4) lib/action_dispatch/middleware/debug_exceptions.rb:17:incall'
web-console (2.2.1) lib/web_console/middleware.rb:39:in call' actionpack (4.2.4) lib/action_dispatch/middleware/show_exceptions.rb:30:incall'
railties (4.2.4) lib/rails/rack/logger.rb:38:in call_app' railties (4.2.4) lib/rails/rack/logger.rb:20:inblock in call'
activesupport (4.2.4) lib/active_support/tagged_logging.rb:68:in block in tagged' activesupport (4.2.4) lib/active_support/tagged_logging.rb:26:intagged'
activesupport (4.2.4) lib/active_support/tagged_logging.rb:68:in tagged' railties (4.2.4) lib/rails/rack/logger.rb:20:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/request_id.rb:21:in call' rack (1.6.4) lib/rack/methodoverride.rb:22:incall'
rack (1.6.4) lib/rack/runtime.rb:18:in call' activesupport (4.2.4) lib/active_support/cache/strategy/local_cache_middleware.rb:28:incall'
rack (1.6.4) lib/rack/lock.rb:17:in call' actionpack (4.2.4) lib/action_dispatch/middleware/static.rb:116:incall'
rack (1.6.4) lib/rack/sendfile.rb:113:in call' railties (4.2.4) lib/rails/engine.rb:518:incall'
railties (4.2.4) lib/rails/application.rb:165:in call' rack (1.6.4) lib/rack/lock.rb:17:incall'
rack (1.6.4) lib/rack/content_length.rb:15:in call' rack (1.6.4) lib/rack/handler/webrick.rb:88:inservice'
/Users/me/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:138:in service' /Users/me/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:94:inrun'
/Users/me/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/webrick/server.rb:294:in `block in start_thread'
Request

Parameters:

{"code"=>"AQA4nDPJLTi3tomX4SdLdUUo15l-Mb-67MCpWC05ZpN_A1N3NvGSFNH7-TsnwiTlC13AXJZ3sNmlxfKNS4bUKu6gTx2scIEhp7FAQPTitjNgCUn7i0C1VHLNrMqROkds3NCK8i6UJt3jis1lbpHDFhbEDtjpL3_y2PH7Y2Ss452MdRykXO9paMXIPCFPnhZ1TSENZPhBEZezHwu7nXvKYWdWP7YGFZ7fK7rM",
"state"=>"d865981afb3fbe4a1cd7d7dd2f5251426bd69191a01ccc92"}

@masterkain
Copy link
Member

what does the rails (or stdout -- something like omniauth/devise like to use) log prints when you get launched on spotify and back?

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

Started GET "/auth/spotify" for 127.0.0.1 at 2015-11-03 12:05:05 +0000
I, [2015-11-03T12:05:05.879805 #10313] INFO -- omniauth: (spotify) Request phase initiated.

Started GET "/auth/spotify" for 127.0.0.1 at 2015-11-03 12:05:06 +0000
I, [2015-11-03T12:05:06.044704 #10313] INFO -- omniauth: (spotify) Request phase initiated.

Started GET "/auth/spotify/callback?code=AQDqQoDNfb2tprc_N5J5xH7qg6nScD00YX0hr3sbo4SVF0tKOUp73I__VC4fL_qAWm66wdfvB67xO-wGQuRRScqM21Wh6B3yhSz-pTDnIfo8Ti0lPByUDcfW-VzFAqfghDBIEIS5RHrF934p2HGL0-XsDD6ZIcQLjmk2P0LWujCHXxQEEPAl1ol3i3Gr3m2cb8Z9yayJiwPmBJVV7VVmvoyTweT-XZ-quZdg&state=ff9512b77cf6cccf8c548f3d036e62294c4112480a567c7a" for 127.0.0.1 at 2015-11-03 12:05:06 +0000
I, [2015-11-03T12:05:06.186506 #10313] INFO -- omniauth: (spotify) Callback phase initiated.
XXXXX

https://accounts.spotify.com/api/token

:raise_errors: true
:parse:
:body:
grant_type: authorization_code
code: AQDqQoDNfb2tprc_N5J5xH7qg6nScD00YX0hr3sbo4SVF0tKOUp73I__VC4fL_qAWm66wdfvB67xO-wGQuRRScqM21Wh6B3yhSz-pTDnIfo8Ti0lPByUDcfW-VzFAqfghDBIEIS5RHrF934p2HGL0-XsDD6ZIcQLjmk2P0LWujCHXxQEEPAl1ol3i3Gr3m2cb8Z9yayJiwPmBJVV7VVmvoyTweT-XZ-quZdg
client_id: xxx
client_secret: xxx
:redirect_uri: http://backupmyplaylistsdev.net/auth/spotify/callback?code=AQDqQoDNfb2tprc_N5J5xH7qg6nScD00YX0hr3sbo4SVF0tKOUp73I__VC4fL_qAWm66wdfvB67xO-wGQuRRScqM21Wh6B3yhSz-pTDnIfo8Ti0lPByUDcfW-VzFAqfghDBIEIS5RHrF934p2HGL0-XsDD6ZIcQLjmk2P0LWujCHXxQEEPAl1ol3i3Gr3m2cb8Z9yayJiwPmBJVV7VVmvoyTweT-XZ-quZdg&state=ff9512b77cf6cccf8c548f3d036e62294c4112480a567c7a
:headers:
Content-Type: application/x-www-form-urlencoded
I, [2015-11-03T12:05:06.698316 #10313] INFO -- : post https://accounts.spotify.com/api/token
D, [2015-11-03T12:05:06.698395 #10313] DEBUG -- request: User-Agent: "Faraday v0.9.2"
Content-Type: "application/x-www-form-urlencoded"
I, [2015-11-03T12:05:06.698461 #10313] INFO -- Status: 400
D, [2015-11-03T12:05:06.698545 #10313] DEBUG -- response: server: "nginx"
date: "Tue, 03 Nov 2015 12:05:06 GMT"
content-type: "application/json"
content-length: "68"
connection: "close"
E, [2015-11-03T12:05:06.699425 #10313] ERROR -- omniauth: (spotify) Authentication failure! invalid_credentials: OAuth2::Error, invalid_grant: Invalid redirect URI
{"error":"invalid_grant","error_description":"Invalid redirect URI"}

OAuth2::Error (invalid_grant: Invalid redirect URI
{"error":"invalid_grant","error_description":"Invalid redirect URI"}):
oauth2 (1.0.0) lib/oauth2/client.rb:117:in request' oauth2 (1.0.0) lib/oauth2/client.rb:142:inget_token'
oauth2 (1.0.0) lib/oauth2/strategy/auth_code.rb:29:in get_token' omniauth-oauth2 (1.4.0) lib/omniauth/strategies/oauth2.rb:89:inbuild_access_token'
omniauth-oauth2 (1.4.0) lib/omniauth/strategies/oauth2.rb:73:in callback_phase' omniauth (1.2.2) lib/omniauth/strategy.rb:227:incallback_call'
omniauth (1.2.2) lib/omniauth/strategy.rb:184:in call!' omniauth (1.2.2) lib/omniauth/strategy.rb:164:incall'
omniauth (1.2.2) lib/omniauth/builder.rb:59:in call' rack (1.6.4) lib/rack/etag.rb:24:incall'
rack (1.6.4) lib/rack/conditionalget.rb:25:in call' rack (1.6.4) lib/rack/head.rb:13:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/params_parser.rb:27:in call' actionpack (4.2.4) lib/action_dispatch/middleware/flash.rb:260:incall'
rack (1.6.4) lib/rack/session/abstract/id.rb:225:in context' rack (1.6.4) lib/rack/session/abstract/id.rb:220:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/cookies.rb:560:in call' activerecord (4.2.4) lib/active_record/query_cache.rb:36:incall'
activerecord (4.2.4) lib/active_record/connection_adapters/abstract/connection_pool.rb:653:in call' activerecord (4.2.4) lib/active_record/migration.rb:377:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/callbacks.rb:29:in block in call' activesupport (4.2.4) lib/active_support/callbacks.rb:88:inrun_callbacks'
activesupport (4.2.4) lib/active_support/callbacks.rb:778:in _run_call_callbacks' activesupport (4.2.4) lib/active_support/callbacks.rb:81:inrun_callbacks'
actionpack (4.2.4) lib/action_dispatch/middleware/callbacks.rb:27:in call' actionpack (4.2.4) lib/action_dispatch/middleware/reloader.rb:73:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/remote_ip.rb:78:in call' actionpack (4.2.4) lib/action_dispatch/middleware/debug_exceptions.rb:17:incall'
web-console (2.2.1) lib/web_console/middleware.rb:39:in call' actionpack (4.2.4) lib/action_dispatch/middleware/show_exceptions.rb:30:incall'
railties (4.2.4) lib/rails/rack/logger.rb:38:in call_app' railties (4.2.4) lib/rails/rack/logger.rb:20:inblock in call'
activesupport (4.2.4) lib/active_support/tagged_logging.rb:68:in block in tagged' activesupport (4.2.4) lib/active_support/tagged_logging.rb:26:intagged'
activesupport (4.2.4) lib/active_support/tagged_logging.rb:68:in tagged' railties (4.2.4) lib/rails/rack/logger.rb:20:incall'
actionpack (4.2.4) lib/action_dispatch/middleware/request_id.rb:21:in call' rack (1.6.4) lib/rack/methodoverride.rb:22:incall'
rack (1.6.4) lib/rack/runtime.rb:18:in call' activesupport (4.2.4) lib/active_support/cache/strategy/local_cache_middleware.rb:28:incall'
rack (1.6.4) lib/rack/lock.rb:17:in call' actionpack (4.2.4) lib/action_dispatch/middleware/static.rb:116:incall'
rack (1.6.4) lib/rack/sendfile.rb:113:in call' railties (4.2.4) lib/rails/engine.rb:518:incall'
railties (4.2.4) lib/rails/application.rb:165:in call' rack (1.6.4) lib/rack/lock.rb:17:incall'
rack (1.6.4) lib/rack/content_length.rb:15:in call' rack (1.6.4) lib/rack/handler/webrick.rb:88:inservice'
/Users/me/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:138:in service' /Users/me/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/webrick/httpserver.rb:94:inrun'
/Users/me/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/webrick/server.rb:294:in `block in start_thread'

Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/_source.erb (7.1ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.0ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.3ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/actionpack-4.2.4/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (64.2ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/_markup.html.erb (0.5ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (0.5ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.4ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.5ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/console.js.erb within layouts/javascript (58.0ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/main.js.erb within layouts/javascript (0.5ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/error_page.js.erb within layouts/javascript (0.8ms)
Rendered /Users/me/.rvm/gems/ruby-2.2.1@playlistconverterv2/gems/web-console-2.2.1/lib/web_console/templates/index.html.erb (134.4ms)

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

maybe there is Started GET "/auth/spotify" twice ?

@masterkain
Copy link
Member

maybe, I just redid the authentication process in dev and I'm afraid it works well for me.
The problem must lie in the app configuration or the path that you are sending to spotify, even because this is just a tiny wrapper around the much larger OAuth2 lib.

13:07:34 web.1     | I, [2015-11-03T13:07:34.945886 #26505]  INFO -- omniauth: (spotify) Request phase initiated.
13:07:34 web.1     | 127.0.0.1 - - [03/Nov/2015:13:07:34 +0100] "GET /users/auth/spotify HTTP/1.1" 302 - 0.0199
13:07:35 web.1     | I, [2015-11-03T13:07:35.518009 #26505]  INFO -- omniauth: (spotify) Callback phase initiated.
13:07:37 web.1     | 127.0.0.1 - - [03/Nov/2015:13:07:37 +0100] "GET /users/auth/spotify/callback?code=xxx&state=4f7e47d68a6d4864933f97317c7e8d1fbe5d33ba1f6c6125 HTTP/1.1" 302 - 1.7267
Started GET "/users/auth/spotify" for 127.0.0.1 at 2015-11-03 13:07:34 +0100

Started GET "/users/auth/spotify/callback?code=xxx&state=4f7e47d68a6d4864933f97317c7e8d1fbe5d33ba1f6c6125" for 127.0.0.1 at 2015-11-03 13:07:35 +0100
Processing by AuthenticationsController#spotify as HTML
  Authentication Load (31.4ms)  SELECT  "authentications".* FROM "authentications" WHERE "authentications"."provider" = $1 AND "authentications"."uid" = $2 LIMIT 1  [["provider", "spotify"], ["uid", "1181346016"]]
...

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

"the app configuration or the path that you are sending to spotify" can you explain a little bit more please ?
thx in advance.

@masterkain
Copy link
Member

I had to do some workarounds in the past on some rails apps to make them work with OmniAuth, for example currently I have this (but not in the project that's working without those changes):

the key was changing full_host and path_prefix

# See also:
#   https://gist.github.com/1236839
# OmniAuth.config.full_host = "https://#{Settings.app.host}"
unless Rails.env.test?
  OmniAuth.config.full_host = ->(env) do
    if Rails.env.staging? || Rails.env.production?
      "https://#{Settings.app.host}"
    else
      if env['omniauth.strategy'].is_a?(OmniAuth::Strategies::Dropbox)
        # Dropbox works only in https atm.
        "https://#{Settings.app.host}"
      else
        "http://#{Settings.app.host}:#{Settings.app.port}"
      end
    end
  end
end
OmniAuth.config.logger = Rails.logger
OmniAuth.config.path_prefix = '/account/auth'

# By default, in development mode, OmniAuth calls this endpoint when /callbacks fails:
# "#{env['SCRIPT_NAME']}#{OmniAuth.config.path_prefix}/failure?message=#{message_key}#{origin_query_param}#{strategy_name_query_param}"
# which in turn calls our /failure endpoint but exploding with an exception.
# This is weird behaviour, so we override the on failure proc to go straight to our
# failure method and execute only that.
OmniAuth.config.on_failure = proc do |env|
  controller_klass = ActiveSupport::Inflector.constantize('OmniauthCallbacksController')
  controller_klass.action(:failure).call(env)
end

@mathieue
Copy link
Author

mathieue commented Nov 3, 2015

Ok. Will take a moment to have a look on that.
Cheers.

@masterkain
Copy link
Member

can you try locking the gem at 0.0.6 and try again? there was a patch that kinda broke it for me but it had something to do with params in callback uris.

@masterkain
Copy link
Member

otherwise keep the latest omniauth-spotify and make sure to bump to the latest version of omniauth-oauth2, it seems they have solved this omniauth/omniauth-oauth2#70

@mathieue
Copy link
Author

mathieue commented Nov 9, 2015

0.0.6 -> down
i've tryed the patch btu have weird behaviour and dont understand it very well.. i have to hack the code to underestand it... and dont have the time.. otherwise i will code my gem...
i have last version of omniauth-oauth2.

@mathieue
Copy link
Author

mathieue commented Nov 9, 2015

for your information i have the same issue with rspotify....

@masterkain
Copy link
Member

maybe you can try this option https://github.com/intridea/omniauth-oauth2/pull/82/files

@mathieue
Copy link
Author

mathieue commented Nov 9, 2015

last one did the trick. perfect :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants