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

Remix auth #47

Merged
merged 45 commits into from
Aug 21, 2023
Merged

Remix auth #47

merged 45 commits into from
Aug 21, 2023

Conversation

sverhoeven
Copy link
Member

@sverhoeven sverhoeven commented Jul 19, 2023

Fixes i-VRESSE/bartender#54

Should be merged together with i-VRESSE/bartender#73

TODO

  • store private key for making bartender tokens
  • use remix auth for local users with remix-auth-form
  • use remix-auth-oauth2 for
    • github
    • orcid sandbox
    • orcid prod
    • egi-checkin
  • associate local user with oauth account aka mail matching
  • regenerated app/bartender-client against JWT token only bartender#73
  • dont generate bartender token every time, but store/fetch from db
  • enforce password length with zod
  • store users in postgresql instead of sqlite
  • Replace expertise level seed with enum as new level needs code change, so why not have levels as code as well.
  • document oauth2 provider setup
  • docker-compose with bartender and haddock3-webapp dbs + rsa key pair mount
  • explain remix + prisma choice
    • workflow builder works, while in webpack based meta framework the build chokes
    • follow remix blue stack template, for least dev surprises
  • Upgraded to latest remix
    • Use v2 route file naming
  • use prisma migrations to create tables in db
  • Calculate gravatar hash

To test:

  1. Start bartender web service from https://github.com/i-VRESSE/bartender/tree/asym-jwt-only branch with public key.
  2. Setup webapp according to development instructions in README.md
  3. Register + login + give yourself an expertise level
  4. Check on build page that topoaa node has options for your selected expertise level
  5. Configure social login GitHub, Orcid or EGI and try to login with those accounts. PS. Make sure you a logged out from the webapp before trying to log in again.
  6. Start webapp with docker compose using Docker instructions in README.m

@sverhoeven sverhoeven mentioned this pull request Jul 19, 2023
4 tasks
@sverhoeven sverhoeven marked this pull request as ready for review August 9, 2023 15:13
Copy link
Contributor

@Peter9192 Peter9192 left a comment

Choose a reason for hiding this comment

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

  • Profile popup doesn't go away when clicked
  • Checkboxes on admin page shouldn't apply directly, but after a save
  • Can't change password on first admin user?
  • Batch checker doesn't work on admin page (with single user)

Comment on lines +11 to +12
A super user can be made through the admin page (`/admin/users`) or by being the first registered user.

Copy link
Contributor

Choose a reason for hiding this comment

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

I accidentily revoked SU rights for the first registered user... that should not be so easy.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Disabling super user toggle when you are the only super user would be nice, but confirm is good enough.

"typecheck": "tsc"
"typecheck": "tsc",
"setup": "prisma generate && prisma migrate deploy && prisma db seed",
"docker:dev": "docker compose -f docker-compose.dev.yml up",
Copy link
Contributor

Choose a reason for hiding this comment

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

It's nice to have this as an npm run alias, but if you stop it (ctrl+c) then the containers will linger on my system (docker ps -a ). I'd like to have either the counterpart with docker compose rm or the docker-compose alternative for docker --rm (is that --abort-on-container-exit?)

Copy link
Contributor

Choose a reason for hiding this comment

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

Additionally, a command to clear the database would be super helpful, at least for developer purposes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can add && docker-compose rm -fsv to script so after you kill the up command the rm command is run.

Copy link
Member Author

Choose a reason for hiding this comment

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

There is no autoclean argument when using just docker compose up. In 0cfa352 added prune command to clean up.

Reset db command was added in 055807a .

Comment on lines +44 to +45
(Stores data in `./postgres-data`)
(You can get a psql shell with `npm run psql:dev`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Add here how to clear the database? And remove the container?

Copy link
Member Author

Choose a reason for hiding this comment

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

Add clear command in 0cfa352 and a2c50c9 to remove container

The database can be initialized with

```sh
npm run setup
Copy link
Contributor

Choose a reason for hiding this comment

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

What about migrations? Do I need to run this command always?

Copy link
Member Author

Choose a reason for hiding this comment

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

Clearified in a2c50c9

README.md Outdated
If not set, a hardcoded secret is used, which should not be used in production.

The data of the login sessions in stored in the `./sessions` directory.
See [docs/auth.md](docs/auth.md).
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be good to say here already that the first user is automatically admin.

README.md Outdated
Comment on lines 167 to 169
The haddock3 web application must be trusted by the bartender web service using a JWT token.
An RSA private key is used by the haddock3 web application to sign the JWT token.
To tell the haddock3 web application where to find the private key, use the `BARTENDER_PRIVATE_KEY` environment variable.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this explananation is a bit confusing. What about:

Suggested change
The haddock3 web application must be trusted by the bartender web service using a JWT token.
An RSA private key is used by the haddock3 web application to sign the JWT token.
To tell the haddock3 web application where to find the private key, use the `BARTENDER_PRIVATE_KEY` environment variable.
The haddock3 web application can prove its identity to the bartender web service using a JWT token.
An RSA private key is used by the haddock3 web application to sign the JWT token.
To tell the haddock3 web application where to find the private key, use the `BARTENDER_PRIVATE_KEY` environment variable.


```shell
npm install
cp .env.example .env
Copy link
Contributor

Choose a reason for hiding this comment

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

Add a note/guideline on changing the secret?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 0cfa352

Copy link
Contributor

@Peter9192 Peter9192 left a comment

Choose a reason for hiding this comment

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

Good stuff! I found a few things in the docker compose file, further mostly some clarification suggestions. While browsing to remix docs, I noticed that they mostly like cookiesessionstorage because you don't need a database for them. So I was wondering, since we have a database anyway, is this really the most suitable choice for storing session info? Furthermore, it would be nice to have a dedicated error page in the same style as the rest of the app. And it would be helpful to clarify (more explicitly) that bartender should not be setting roles, this is done by the web app

Comment on lines 65 to 67
- type: bind
source: ./private_key.pem
target: /app/src/private_key.pem
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the web app be the sole owner (and user) of the private key? Bartender should only need the public key to verify the token, right?

Suggested change
- type: bind
source: ./private_key.pem
target: /app/src/private_key.pem
- type: bind
source: ./public_key.pem
target: /app/src/public_key.pem

environment:
BARTENDER_API_URL: "http://bartender:8000"
DATABASE_URL: postgresql://postgres:postgres@webappdb:5432/postgres
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
DATABASE_URL: postgresql://postgres:postgres@webappdb:5432/postgres
DATABASE_URL: postgresql://postgres:postgres@webappdb:5432/postgres
BARTENDER_PRIVATE_KEY: /app/src/private_key.pem

Copy link
Member Author

Choose a reason for hiding this comment

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

Added in 6cfcd2e

Comment on lines 23 to 24
source: ./public_key.pem
target: /app/src/public_key.pem
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the web app need the private key to generate tokens?

Suggested change
source: ./public_key.pem
target: /app/src/public_key.pem
source: ./private_key.pem
target: /app/src/private_key.pem

id="password"
name="password"
type="password"
minLength={8}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this min length check on login as well? Since you can't have a password of <8 characters, the password would be wrong anyway. But it's not like this is checking it's valid.

Copy link
Member Author

Choose a reason for hiding this comment

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

You are right, removed length check on server and client in 6cfcd2e

README.md Outdated
Comment on lines 25 to 26
## Setup

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe start with a note that you need to have a running instance of bartender setup before starting this? And point to the section below on how to set it up?

Comment on lines 41 to 42
// If bartender has been configured with allowed_roles for an application,
// then the a role claim should be in the JWT.
Copy link
Contributor

Choose a reason for hiding this comment

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

That useful info is a bit hidden 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 25 to 34
<input
type="checkbox"
checked={user.isSuperuser}
checked={user.isAdmin}
className="checkbox"
disabled={submitting}
onChange={() => {
const data = new FormData();
data.set("isSuperuser", user.isSuperuser ? "false" : "true");
data.set("isAdmin", user.isAdmin ? "false" : "true");
onUpdate(data);
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Disable if there's only one admin left?

Comment on lines 68 to 72
<td>
<button className="btn-sm btn" disabled>
Change password
</button>
</td>
Copy link
Contributor

Choose a reason for hiding this comment

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

Confusing to have placeholders that are not implemented without explicitly stating so.

Comment on lines 20 to 22
<td>
<input type="checkbox" disabled />
</td>
Copy link
Contributor

Choose a reason for hiding this comment

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

Unclear why this is disabled by default. Remove or explain?

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed in fcc7c68

export async function listRoles(accessToken: string) {
const api = buildRolesApi(accessToken);
return await api.listRoles();
export async function assignExpertiseLevel(
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this also upgrade their jwt token?

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope expertise level is not in token

@sverhoeven sverhoeven mentioned this pull request Aug 21, 2023
@sverhoeven
Copy link
Member Author

Good stuff! I found a few things in the docker compose file, further mostly some clarification suggestions. While browsing to remix docs, I noticed that they mostly like cookiesessionstorage because you don't need a database for them. So I was wondering, since we have a database anyway, is this really the most suitable choice for storing session info? Furthermore, it would be nice to have a dedicated error page in the same style as the rest of the app. And it would be helpful to clarify (more explicitly) that bartender should not be setting roles, this is done by the web app

The users browser needs to persist something between page loads we are storing the encrypted user id in the cookie just like https://github.com/remix-run/blues-stack/blob/main/app/session.server.ts#L78 . If you would store session id in cookie and session to user mapping as a db table there are more moving parts without much benefit.

For errors you already created #22 so will deal with better error pages when fixing that issue.

Thanks for reviewing this monster pull request and fixing the deployment error I should have caught.

@sverhoeven sverhoeven merged commit 51c98f9 into main Aug 21, 2023
1 check passed
@sverhoeven sverhoeven deleted the remix-auth branch August 21, 2023 12:35
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.

Outsource user management
2 participants