Skip to content

Latest commit

 

History

History
298 lines (205 loc) · 11.8 KB

MIGRATION-GUIDE-v1.md

File metadata and controls

298 lines (205 loc) · 11.8 KB

Migration Guide v1

This guide is to share a few helpful tips to migrate a production app using rodauth-oauth v0.10 to v1. There are quite a few breaking changes which will require some work to pull through, and I'll go 1 by 1.

Most important tip: Make sure you're running v0.10.3 before you try to migrate to v1!

Minimum ruby version: 2.5

Make sure you're at least running ruby 2.5 before considering migrating.

The Oauth Token Resource was removed

The access and refresh tokens are now stored in the oauth_grants table, and the oauth_tokens table is no longer necessary.

This means that:

  • Both the table, columns and labels config options prefixed by oauth_tokens were removed.
  • "Oauth Token Revocation" becomes "OAuth Grant Revocation", and all relevant options and text changed accordingly.
  • OAuth management plugins also change (i.e. URL path for listing grants is /oauth-grants)

How to migrate

The tl;dr is: you'll need to start by adding the new columns to the oauth_grants table, start backfilling the values in runtime, backfill historical grants in the background, then deploy v1.

This example is going to use token and refresh_token columns as an example, but it's extensible to token_hash and refresh_token_hash.

Add the required token columns to the oauth_grants table and start backfilling themm:

# example sequel migration

Sequel.migration do
  up do
    alter_table :oauth_grants do
      String :code, nullable: true # to change the nullable constraint
      String :token, nullable: true, unique: true
      String :refresh_token, nullable: true, unique: true
    end


    # Then the runtime backfilling can happen. You can do that using a trigger on the `oauth_tokens` table

    run <<-SQL
CREATE OR REPLACE FUNCTION copy_tokens_to_grant()
  RETURNS trigger AS
$$
BEGIN
  UPDATE "oauth_grants" SET "oauth_grants"."token" = NEW."token",
    "oauth_grants"."refresh_token" = NEW."refresh_token",
    "oauth_grants"."expires_in" = NEW."expires_in",
    "oauth_grants"."code" = NULL
    WHERE "oauth_grants"."id" = NEW."oauth_grant_id";

RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';

CREATE TRIGGER copy_tokens_to_grant
  AFTER INSERT
  ON oauth_tokens
  FOR EACH ROW
  EXECUTE PROCEDURE copy_tokens_to_grant();
    SQL
  end
end

Then you'll need too backfill grants created before the changes above.

--- this is how to do it in SQL, but can also be accomplished with a ruby script looping on rows.

UPDATE "oauth_grants"
  INNER JOIN "oauth_tokens" ON "oauth_tokens"."oauth_grant_id" = "oauth_grants"."id"
  SET "oauth_grants"."token" = "oauth_tokens"."token",
    "oauth_grants"."refresh_token" = "oauth_tokens"."refresh_token",
    "oauth_grants"."expires_in" = "oauth_tokens"."expires_in",
    "oauth_grants"."code" = NULL
  WHERE "oauth_grants"."token" IS NULL;

And now you can deploy the app with v1 installed (after you've done the required changes).

oauth applications: oauth_applications_client_secret_hash_column

The client secret is hashed (with bcrypt) before being stored, by default. While previously it was also hashed by default, this is now driven by the new :oauth_applications_client_secret_hash_column options, which is set to :client_secret. In order to disable hashing and store the client secret in plain-text (smth whic, p.ex. the client_secret_jwt auth method requires), just do this:

oauth_applications_client_secret_hash_column nil

oauth applications: oauth_applications_homepage_url_column no longer required

The homepage url is no longer considered a require prooperty of an OAuth client application.

oauth grants: access token and refresh token hashed by default

access token and refresh token columns are now hashed by default, and point to the same column as the main counterpart:

# this is now the default
oauth_grants_token_hash_column :token
oauth_grants_token_column :token
oauth_grants_refresh_token_hash_column :refresh_token
oauth_grants_refresh_token_column :refresh_token

in order to keep storing the tokens in plaintext, set the hash column options tonil. In order to keep storing the hashed token in a separate coluumn, just redefine it to the name of the column.

oauth_response_mode is now form_post.

To get the old behaviour back:

oauth_response_mode "query"

renamed options

The following auth config methods were renamed (rename them if you're redefining them):

  • description_param (replaced by oauth_applications_description_param)
  • client_id_param (replaced by oauth_applications_client_id_param)
  • oauth_applications_jws_jwk_column (replaced by oauth_applications_jwks_column)
  • oauth_applications_jws_jwk_label (replaced by oauth_applications_jwks_label)
  • oauth_application_jws_jwk_param (replaced by oauth_applications_jwks_param)
  • all config methods terminated in "_error_status" are now prefixed by "oauth_"
  • all config methods terminated in "_message" are now prefixed by "oauth_
  • all config methods terminated in "_error_code`` are now prefixed by "oauth"`
  • unique_error_message config method was removed (not in use)
  • oauth_jwt_token_issuer renamed to oauth_jwt_issuer
  • oauth_auth_methods_supported renamed to oauth_token_endpoint_auth_methods_supported
  • oauth_jwt_algorithms_supported renamed to oauth_jwt_jws_algorithms_supported
  • oauth_token_expires_in renamed to oauth_access_token_expires_in

Removed options

Base options

  • oauth_application_default_scope: if you were using it to pre-fill scopes in the Authorization form, or the New OAuth Application form, you'll have to do it yourself.

JWT options

  • oauth_jwt_key: can be replaced by using it as the value in oauth_jwt_keys (oauth_jwt_keys("RS256" => [privkey]));
  • oauth_jwt_algorithm: can be replaced by using it as the key in oauth_jwt_keys (oauth_jwt_keys("RS256" => [privkey]));
  • oauth_jwt_public_key: can be replaced by using it as the value in oauth_jwt_public_keys (oauth_jwt_public_keys("RS256" => [pubkey]));
  • oauth_jwe_key: can be replaced by using it as the value in oauth_jwe_keys (oauth_jwe_keys((%w[RSA-OAEP A128CBC-HS256] => [privkey]));
  • oauth_jwt_jwe_algorithm: can be replaced by using it as the first element in the key tuple in oauth_jwe_keys (oauth_jwe_keys(%w[RSA-OAEP A128CBC-HS256] => [privkey]));
  • oauth_jwt_jwe_encryption_method: can be replaced by using it as the second element in the key tuple in oauth_jwe_keys (oauth_jwe_keys(%w[RSA-OAEP A128CBC-HS256] => [privkey]));
  • oauth_jwe_public_key: can be replaced by using it as the value in oauth_jwe_public_keys (oauth_jwt_public_keys(%w[RSA-OAEP A128CBC-HS256] => [pubkey]));
  • oauth_jwt_legacy_algorithm: can be replaced by adding it as a key to oauth_jwt_public_keys;
  • oauth_jwt_legacy_public_key: can be replaced by adding it to the value set of oauth_jwt_public_keys, after the current key (oauth_jwt_public_keys("RS256" => [pubkey, legacy_pubkey]));

oauth_jwt_key

oauth_device_grant feature becomes oauth_device_code_grant

In case you were using it directly, you should rename it.

use_oauth_*_grant options were removed

One of the main changes in v1.0.0 is that one should enable the features one needs, explicitly. So when you used to have:

rodauth do
  enable :oauth
end

you should now load the grants you use:

rodauth do
  enable :oauth_authorization_code_grant, :oauth_pkce, :oauth_credentials_grant, :oauth_token_introspection
  # or
  enable :oidc, :oauth_implicit_grant
end

Now that the features are explicitly enable, there's is no more use for config methods for unsetting them, such as use_oauth_implicit_grant_type? or use_oauth_pkce?.

oauth_jwt_audience and oauth_jwt_issuer repurposed as functions

To maintain legacy behaviour, you can return them in the function body.

- oauth_jwt_audience legacy_aud
- oauth_jwt_issuer legacy_iss
+ oauth_jwt_audience { legacy_aud }
+ oauth_jwt_issuer { legacy_iss }

JAR (Secured authorization request) segregated in its plugin

It was previously being loaded in the :oauth_jwt plugin by default. If you require this funtionality, enable the plugin:

enable :oauth_jwt_secured_authorization_request

oauth_jwt_jwks plugin

JWKs URI endpoint has been moved to its plugin. If you require this functionality, make sure you enable it:

enable :oauth_jwt, :oauth_jwt_jwks

OIDC RP-initiated logout segregated in its plugin

It was previously being loaded in the :oidc plugin by default. If you require this funtionality, enable the plugin:

enable :oidc_rp_initiated_logout

routing functions renamed

Previously, loading well-known routes, the oauth server metadata, or oauth application/tokens (now grants) management dashboard implied calling a function on roda to load those routes. These have been renamed:

plugin :rodauth do
  enable :oidc, :oauth_application_management, :oauth_grant_management
end

roda do |r|
-  rodauth.oauth_applications
-  rodauth.oauth_grants
-  rodauth.openid_configuration
-  rodauth.webfinger
+  rodauth.load_oauth_application_management_routes
+  rodauth.load_oauth_grant_management_routes
+  rodauth.load_openid_configuration_route
+  rodauth.load_webfinger_route
end

resource server mode via oauth_resource_server feature

You should now be able to set a resource server just using this plugin, instead of the combination of config tweaks previously suggested.

plugin :rodauth do
-  enable :oauth
-  is_authorization_server? false
  enable :oauth_resource_server
  authorization_server_url "https://external-auth-server"
end

oauth_token_subject returns client id for client-application tokens

Previously, if a token from a client credentials grant would be used, calling oauth_token_subject would return the oauth application primary key. It now returns the application client id.

oauth_jwt_subject* family of options moved to oidc feature

Previously, they were in the oauth_jwt feature; however, they're not specced for use in general purpose JWT Access Tokens, but rather in OIDC ID tokens.

oauth feature removed

The oauth plugin, which is how this gem started, was a giant "god" feature, which has been gradually broken down into sub-features, each implementing an RFC or specific feature, and building on top of each other. In its last state, it was just loading all those sub-features, for backwards-compatibility; and when you need to turn off a particular feature, you'd have to set a use_oauth_implicit_grant_type? type of config to false.

That is now over, and you'll need to explicitly load all features you need yourself.

plugin :rodauth
-  enable :oauth
-  use_oauth_implicit_grant_type? false
+  enable :oauth_authorization_code_grant, :oauth_client_credentials_grant
end

require_oauth_application will not support "none" strategy by default

Unless explicitly set in oauth application config, or oauth_token_endpoint_auth_methods_supported config.

jwt_bearer_grant feature exposes client_secret_jwt and private_key_jwt as token endpoint auth methods

While the latter is a new feature, the former was already implemented, but not declared.

Webfinger options

webfinger_relation has been removed. If you were overriding it, you can override json_webfinger_payload to backport this behaviour.

PKCE is strict by default

This was a security improvement. However, if you were relying on, set oauth_require_pkce to false.

refresh token policy set to "rotation" by default

This was a security improvement. However, if you were relying on, set oauth_refresh_token_protection_policy to "none".

using access tokens won't set rodauth session

Which means that rodauth won't identify you as "logged in". If you were relying on this behaviour, you'll have to tweak one of the available rodauth options.