Skip to content

Feature/6207 Endpoint api/v1/sign_in now outputs randomized tokens in response upon sign-in#6216

Merged
FireLemons merged 12 commits into
rubyforgood:mainfrom
rubyforpeace:feature/6207
Feb 19, 2025
Merged

Feature/6207 Endpoint api/v1/sign_in now outputs randomized tokens in response upon sign-in#6216
FireLemons merged 12 commits into
rubyforgood:mainfrom
rubyforpeace:feature/6207

Conversation

@7riumph
Copy link
Copy Markdown
Collaborator

@7riumph 7riumph commented Feb 6, 2025

What github issue is this PR for, if any?

Resolves #6207

What changed, and why?

Volunteers api/v1/sign_in endpoint now outputs secured randomized tokens upon sign-in.

  • Dropped token column from users table
  • Added api_credentials table to be used for users model for greater separation of concerns (SoC)
  • Utilized SHA-256 hash to ensure raw tokens are never stored in db ( api_token_digest & refresh_token_digest )
  • Created models/api_credentials and concern/api.rb for further api concern separation and utility

Why?: To ensure compliance with apples review process, and enhance security with an applicable industry standard.

How is this tested? (please write tests!) 💖💪

New tests:

Authentication Helper Function tests (14 in total) → spec/models/api_credential_spec.rb
Credentials Factory Definition → spec/factories/api_credential.rb

Updated tests ( no more token column ):

BaseController test → spec/requests/api/v1/base_spec.rb
SessionController test → spec/requests/api/v1/users/sessions_spec.rb
Users Factory Definition → spec/factories/users.rb

Deployment & Documentation:

Deployment task → lib/tasks/deployment/20230822145532_populate_api_tokens.rake
Swagger Documentation → swagger/v1/swagger.yaml
More Swagger→ spec/swagger_helper.rb

Screenshots please :)

Example output with updated api/v1/sign_in endpoint blueprint

example_output_sign_in_success

Feelings gif (optional)

very strange

@7riumph 7riumph changed the title Feature/6207 Feature/6207 Endpoint api/v1/sign_in now outputs randomized tokens in response upon sign-in Feb 6, 2025
@7riumph 7riumph marked this pull request as draft February 6, 2025 23:17
@7riumph 7riumph changed the title Feature/6207 Endpoint api/v1/sign_in now outputs randomized tokens in response upon sign-in Feature/6207 Endpoint api/v1/sign_in now secured via randomized tokens in response upon sign-in Feb 6, 2025
@7riumph 7riumph changed the title Feature/6207 Endpoint api/v1/sign_in now secured via randomized tokens in response upon sign-in Feature/6207 Endpoint api/v1/sign_in now secured with randomized tokens in response upon sign-in Feb 6, 2025
@7riumph 7riumph changed the title Feature/6207 Endpoint api/v1/sign_in now secured with randomized tokens in response upon sign-in Feature/6207 Endpoint api/v1/sign_in now outputs randomized tokens in response upon sign-in Feb 6, 2025
@7riumph 7riumph requested a review from xihai01 February 6, 2025 23:27
@xihai01
Copy link
Copy Markdown
Collaborator

xihai01 commented Feb 7, 2025

looks good to me so far 🔥

I think instead of storing the token and refresh token as plaintext in the db, we can store it as a hash
like what shen suggested

Then the client would be provided the plaintext versions of the tokens and whenever they call the api, the hash versions will be compared

Comment thread app/controllers/api/v1/users/sessions_controller.rb Outdated
if @user

@user.regenerate_session_token!
@user.regenerate_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.

don't do this twice?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It is actually for two separate tokens. The plan is the session_token expires quickly and is repeatedly replaced for as long as the refresh_token is valid to maintain access.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Token regeneration is now handled in the blueprint before being passed to the session controller.

Comment thread app/models/supervisor.rb Outdated
# receive_email_notifications :boolean default(TRUE)
# receive_reimbursement_email :boolean default(FALSE)
# receive_sms_notifications :boolean default(FALSE), not null
# refresh_token :string
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.

maybe make a new type of user, ApiUser?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Looking into this, may complicate authorization. I'll just create a completely new table for api/v1 credentials so the user model and roles inheriting from it aren't overloaded.

Additionally, will attempt to do more compartmentalizing this PR through a concern :)

Copy link
Copy Markdown
Collaborator Author

@7riumph 7riumph Feb 11, 2025

Choose a reason for hiding this comment

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

Created the concern and imported into models/user.rb

Comment thread app/models/user.rb Outdated
after_create :create_preference_set
before_update :record_previous_email
has_secure_token :token, length: 36
has_secure_token :refresh_token, length: 36
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.

36 what? seconds, minutes, days, weeks?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Vague syntax refers to token length, in this case. The token is 36 characters long.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Tokens are now in a credentials table

before_save :generate_refresh_token

# Securely confirm/deny that Hash in db is same as current users token Hash
def authenticate_api_token(api_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.

does these functions return boolean value?
if so, the naming should be consistent with other similar functions starting with the is_....

Copy link
Copy Markdown
Collaborator Author

@7riumph 7riumph Feb 13, 2025

Choose a reason for hiding this comment

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

Yes, returns a boolean. How's Something like "is_api_token_valid"?

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.

yes that would be perfect
also follows ruby conventions too

def change
create_table :api_credentials do |t|
t.references :user, null: false, foreign_key: true
t.string :api_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.

you're also storing the plain text tokens in the table?

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.

nvm I think you created a migration to remove them right?

Copy link
Copy Markdown
Collaborator Author

@7riumph 7riumph Feb 13, 2025

Choose a reason for hiding this comment

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

Yeah, originally added 2 unneeded token columns then created a migration to remove them.

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.

cool


# Put your task implementation HERE.
User.where(token: nil).each do |user|
User.find_each do |user|
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.

is it possible to create a separate deployment task for populating the api tokens?
so we don't touch the sms stuff

Copy link
Copy Markdown
Collaborator Author

@7riumph 7riumph Feb 13, 2025

Choose a reason for hiding this comment

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

Get the concern, but this task was not made as part of the original SMS epic. History is you originally created the file here then Sam Williams or schoork added our sms and your token addition to it here

I've edited the task because tokens are in a api_credentials table now.

Copy link
Copy Markdown
Collaborator

@xihai01 xihai01 Feb 13, 2025

Choose a reason for hiding this comment

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

I see I see
I forgot I originally created this
yea, just leave it then

Copy link
Copy Markdown
Collaborator

@xihai01 xihai01 left a comment

Choose a reason for hiding this comment

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

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ruby Touches Ruby code 🧪 Tests Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance endpoint security for /api/v1/users/sign_in with randomization

4 participants