# frozen_string_literal: true require "spec_helper" describe "Refresh Token Flow" do before do Doorkeeper.configure do orm DOORKEEPER_ORM use_refresh_token end client_exists end context "issuing a refresh token" do before do authorization_code_exists application: @client end it "client gets the refresh token and refreshes it" do post token_endpoint_url(code: @authorization.token, client: @client) token = Doorkeeper::AccessToken.first should_have_json "access_token", token.token should_have_json "refresh_token", token.refresh_token expect(@authorization.reload).to be_revoked post refresh_token_endpoint_url(client: @client, refresh_token: token.refresh_token) new_token = Doorkeeper::AccessToken.last should_have_json "access_token", new_token.token should_have_json "refresh_token", new_token.refresh_token expect(token.token).not_to eq(new_token.token) expect(token.refresh_token).not_to eq(new_token.refresh_token) end end context "refreshing the token" do before do @token = FactoryBot.create( :access_token, application: @client, resource_owner_id: 1, use_refresh_token: true ) end context "refresh_token revoked on use" do it "client request a token with refresh token" do post refresh_token_endpoint_url( client: @client, refresh_token: @token.refresh_token ) should_have_json( "refresh_token", Doorkeeper::AccessToken.last.refresh_token ) expect(@token.reload).not_to be_revoked end it "client request a token with expired access token" do @token.update_attribute :expires_in, -100 post refresh_token_endpoint_url( client: @client, refresh_token: @token.refresh_token ) should_have_json( "refresh_token", Doorkeeper::AccessToken.last.refresh_token ) expect(@token.reload).not_to be_revoked end end context "refresh_token revoked on refresh_token request" do before do allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false) end it "client request a token with refresh token" do post refresh_token_endpoint_url( client: @client, refresh_token: @token.refresh_token ) should_have_json( "refresh_token", Doorkeeper::AccessToken.last.refresh_token ) expect(@token.reload).to be_revoked end it "client request a token with expired access token" do @token.update_attribute :expires_in, -100 post refresh_token_endpoint_url( client: @client, refresh_token: @token.refresh_token ) should_have_json( "refresh_token", Doorkeeper::AccessToken.last.refresh_token ) expect(@token.reload).to be_revoked end end context "public & private clients" do let(:public_client) do FactoryBot.create( :application, confidential: false ) end let(:token_for_private_client) do FactoryBot.create( :access_token, application: @client, resource_owner_id: 1, use_refresh_token: true ) end let(:token_for_public_client) do FactoryBot.create( :access_token, application: public_client, resource_owner_id: 1, use_refresh_token: true ) end it "issues a new token without client_secret when refresh token was issued to a public client" do post refresh_token_endpoint_url( client_id: public_client.uid, refresh_token: token_for_public_client.refresh_token ) new_token = Doorkeeper::AccessToken.last should_have_json "access_token", new_token.token should_have_json "refresh_token", new_token.refresh_token end it "returns an error without credentials" do post refresh_token_endpoint_url(refresh_token: token_for_private_client.refresh_token) should_not_have_json "refresh_token" should_have_json "error", "invalid_grant" end it "returns an error with wrong credentials" do post refresh_token_endpoint_url( client_id: "1", client_secret: "1", refresh_token: token_for_private_client.refresh_token ) should_not_have_json "refresh_token" should_have_json "error", "invalid_client" end end it "client gets an error for invalid refresh token" do post refresh_token_endpoint_url(client: @client, refresh_token: "invalid") should_not_have_json "refresh_token" should_have_json "error", "invalid_grant" end it "client gets an error for revoked access token" do @token.revoke post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token) should_not_have_json "refresh_token" should_have_json "error", "invalid_grant" end it "second of simultaneous client requests get an error for revoked access token" do allow_any_instance_of(Doorkeeper::AccessToken).to receive(:revoked?).and_return(false, true) post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token) should_not_have_json "refresh_token" should_have_json "error", "invalid_grant" end end context "refreshing the token with multiple sessions (devices)" do before do # enable password auth to simulate other devices config_is_set(:grant_flows, ["password"]) config_is_set(:resource_owner_from_credentials) do User.authenticate! params[:username], params[:password] end create_resource_owner _another_token = post password_token_endpoint_url( client: @client, resource_owner: @resource_owner ) last_token.update_attribute :created_at, 5.seconds.ago @token = FactoryBot.create( :access_token, application: @client, resource_owner_id: @resource_owner.id, use_refresh_token: true ) @token.update_attribute :expires_in, -100 end context "refresh_token revoked on use" do it "client request a token after creating another token with the same user" do post refresh_token_endpoint_url( client: @client, refresh_token: @token.refresh_token ) should_have_json "refresh_token", last_token.refresh_token expect(@token.reload).not_to be_revoked end end context "refresh_token revoked on refresh_token request" do before do allow(Doorkeeper::AccessToken).to receive(:refresh_token_revoked_on_use?).and_return(false) end it "client request a token after creating another token with the same user" do post refresh_token_endpoint_url( client: @client, refresh_token: @token.refresh_token ) should_have_json "refresh_token", last_token.refresh_token expect(@token.reload).to be_revoked end end def last_token Doorkeeper::AccessToken.last_authorized_token_for( @client.id, @resource_owner.id ) end end end