Skip to content

[NPQ-3490] - store long lived auth token#3317

Open
spqr-praetoria wants to merge 1 commit into
mainfrom
NPQ-3490-store-long-lived-auth-token
Open

[NPQ-3490] - store long lived auth token#3317
spqr-praetoria wants to merge 1 commit into
mainfrom
NPQ-3490-store-long-lived-auth-token

Conversation

@spqr-praetoria
Copy link
Copy Markdown
Contributor

@spqr-praetoria spqr-praetoria commented May 5, 2026

Context

Ticket: https://dfedigital.atlassian.net/browse/NPQ-3490

Changes proposed in this pull request

@spqr-praetoria spqr-praetoria requested a review from a team as a code owner May 5, 2026 12:16
@spqr-praetoria spqr-praetoria changed the title [NPQ-3490] - store long live auth token [NPQ-3490] - store long lived auth token May 5, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 5, 2026

@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch from fd8ba6c to 01efa4e Compare May 5, 2026 13:38
@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch 2 times, most recently from 895d923 to 0ef27e0 Compare May 5, 2026 15:55
@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch from 0ef27e0 to 345820b Compare May 6, 2026 07:07
@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch 3 times, most recently from 03ae5b4 to ed6c714 Compare May 6, 2026 07:47
@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch 3 times, most recently from 82ceb3c to 65339ad Compare May 18, 2026 11:27
@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch 3 times, most recently from 2c04143 to 8050cbe Compare May 19, 2026 08:47
@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch from 8050cbe to c4768b8 Compare May 19, 2026 09:01
@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch from c4768b8 to a94bdb2 Compare May 19, 2026 10:24
@github-actions
Copy link
Copy Markdown
Contributor

Review app for PR 3317 was deleted

jebw
jebw previously approved these changes May 19, 2026
Copy link
Copy Markdown
Collaborator

@jebw jebw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good - I've a few comments on changes - can you review them then ping me when you're ready for a catch up, thanks

Comment on lines -74 to -78
let(:older_statement_output_fee_true) { create(:statement, cohort: older_cohort, lead_provider:, output_fee: true, for_date: 3.months.ago) }
let(:older_statement_output_fee_false) { create(:statement, cohort: older_cohort, lead_provider:, output_fee: false, for_date: 2.months.ago) }
let(:latest_statement_output_fee_true) { create(:statement, cohort: older_cohort, lead_provider:, output_fee: true, for_date: 1.month.ago) }
let(:latest_statement_output_fee_false) { create(:statement, cohort: older_cohort, lead_provider:, output_fee: false, for_date: 0.months.ago) }
let(:future_statement_output_fee_true) { create(:statement, cohort: older_cohort, lead_provider:, output_fee: true, for_date: 1.month.from_now) }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be here - this reverts Alkesh' recent fix for a flaky spec

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will update

it "does not enqueue a RefreshUserTokenJob" do
expect { subject }.not_to have_enqueued_job(RefreshUserTokenJob)
end
end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a spec here to check sentry was alerted that we didn't get a refresh token back for some reason

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think you'll also need to change it to alert sentry


new_refresh_token_assigned = false
user = nil
Rails.logger.info("[HELLO4] Create user with provider data")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

????

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed leftover logging

headers: { "Content-Type" => "application/x-www-form-urlencoded" },
)

response.parsed_response["refresh_token"] || @refresh_token
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes no sense - if the API returned a success resposnse, then @refresh_token is now used up and cannot be resused again so returning it will lead to the application incorrectly updating with an expired token

application.update!(lead_provider_approval_status: "rejected", reason_for_rejection:)
ApplicationRecord.transaction do
application.update!(lead_provider_approval_status: "rejected", reason_for_rejection:)
clear_refresh_token_if_orphaned
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orphaned isn't a term we're using so may lead to confusion for other developers - how about clear_refresh_token_if_no_remaining_applications!

class RefreshAccessToken
include HTTParty

default_timeout 5.seconds
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this will be getting called from a Job I think we can give this longer to respond - say 15.seconds

new_refresh_token_assigned = persist_refresh_token(user_matched_using_trn, provider_data)
end

schedule_refresh(user_matched_using_trn) if new_refresh_token_assigned
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed if we're using an hourly job

Comment on lines +111 to +113
def schedule_refresh(user)
RefreshUserTokenJob.set(wait: 7.days).perform_later(user.id)
end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cron job supercedes this

@@ -3,6 +3,7 @@
module Users
class FindOrCreateFromTeacherAuth
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call method in this class are getting very complicated and its because there are multiple places all trying to do the same 'after sorting user out' action of persisting the token.

I can see 2 approaches

  • Move the persist the token out to the omniauth controller (with some helper code on the token class) - this logic is orthogonal to signing the user in.
@user = Users::FindOrCreateFromTeacherAuth
if @user
  @user.set_refresh_token(provider_data.credentials&.fetch(:token)) if @user.trn.blank?
  # ....
end
  1. Or alternatively, add a level of indirection within this class - move the existing #call method to be a differently named private method, eg sign_in_user or similar since that always returns a user class. Then add a new #call method with something like the following
def call
  sign_in_user(...).tap do |user|
    set_refresh_token_on_user(user) if user && user.trn.blank?
  end
end

Comment on lines +3 to +8
create_table :user_refresh_tokens do |t|
t.references :user, null: false, foreign_key: true, index: { unique: true }
t.text :refresh_token, null: false
t.datetime :refresh_token_updated_at, null: false
t.timestamps
end
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could do with a chat around the schema on this

@jebw jebw dismissed their stale review May 19, 2026 13:22

Meant to comment not approve

Copy link
Copy Markdown
Collaborator

@jebw jebw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See responses in earlier approval (I'd meant to just comment rather than approve so I've dismissed that one)

token_record.update!(refresh_token: new_refresh_token, refresh_token_updated_at: Time.current)
rescue StandardError => e
Sentry.capture_exception(e, extra: { user_id: })
raise
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't re-raise otherwise you'll end up with a thundering herd problem when you chain jobs

  1. 9am - Enqueue job kicks of 1000 api jobs
  2. they fail because endpoint is unavailable
  3. those 1000 jobs reschedule themselves
  4. and keep rescheduling them selves with increasing backoff
  5. meanwhile at 10am - Enqueue job kicks off the same 1000 api jobs for a second time
  6. and so the loop continues

@spqr-praetoria spqr-praetoria force-pushed the NPQ-3490-store-long-lived-auth-token branch from 900ec36 to 618c5a0 Compare May 19, 2026 14:50
@sonarqubecloud
Copy link
Copy Markdown

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

Successfully merging this pull request may close these issues.

2 participants