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

Convert dialog to authorize web session to regular tab and handle expired certs #49754

Merged
merged 11 commits into from
Dec 9, 2024

Conversation

gzdunek
Copy link
Contributor

@gzdunek gzdunek commented Dec 4, 2024

Closes #43328

To make handling device trust errors easier, the dialog to authorize a web session has been converted to a regular tab. Previously, we couldn't handle "expired certs" errors because a relogin dialog would overwrite the dialog to authorize the session. There are ways to overcome it, but the regular tab provides better UX.

image

changelog: The web session authorization dialog in Teleport Connect is now a dedicated tab, which properly shows a re-login dialog when the local session is expired.

Note to reviewers: when testing session expiration, you may want to use max_session_ttl longer than 3m1s.

Copy link
Member

@ravicious ravicious left a comment

Choose a reason for hiding this comment

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

Tested this and it seems to work fine. I found a minor issue with restoring the location of the workspace.

rootCluster: Cluster,
webSessionRequest: WebSessionRequest
): string {
const processedRedirectUri = processRedirectUri(
Copy link
Member

Choose a reason for hiding this comment

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

Why is processRedirectUri called only by buildUnauthorizedSessionUrl?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's because in buildUnauthorizedSessionUrl we don't return redirect_uri but instead a full path that we open directly.
This was originally done this way in #47221, I didn't change it.

const documentsToPersist = (
workspace.previous?.documents || workspace.documents
).filter(d => d.kind !== 'doc.authorize_web_session');

stateToSave.workspaces[w] = {
localClusterUri: workspace.localClusterUri,
location: workspace.previous?.location || workspace.location,
Copy link
Member

Choose a reason for hiding this comment

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

location might point to a document which has been removed. Instead of handling this each time a workspace state is saved, I think we should just check it once when opening the app. When reopening previous session, we could check if the doc that location points to exists, if not then we should use the first doc as the location.

Copy link
Contributor Author

@gzdunek gzdunek Dec 5, 2024

Choose a reason for hiding this comment

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

Ah, good point with the location! The main reason I handled filtering out the documents before saving was this comment:

On top of that, if we don't take any extra precautions, converting this modal into a tab means that data passed from the Web UI to Connect through the custom scheme URL would get saved on disk. Connect automatically saves the state of all open tabs on disk so that it's able to reopen tabs on the next launch.

IIRC, the security model of Device Trust already accounts for this kind of data exfiltration. But it'd probably be a good idea to not persist this particular tab on disk in the first place.

The only kind of attack I can think of is authorizing a session with the user's confirmation. But you could only do it for a valid web request for a valid cluster, so I'm not sure if there's any benefit of that for an attacker.
I agree that it's better to filter out the document when opening the app. However, maybe we should still clear the token and id from the document before saving to disk?

Copy link
Member

Choose a reason for hiding this comment

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

Filtering the documents before saving is fine. I meant that instead of adjusting persistState so that it checks location, we could check if location is valid only when restoring the previous session.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see, I will modify the fix I pushed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Restored previous code and added a test.

lib/teleterm/daemon/daemon.go Outdated Show resolved Hide resolved
@gzdunek gzdunek removed the request for review from flyinghermit December 4, 2024 15:15
@gzdunek gzdunek requested a review from ravicious December 5, 2024 13:18
Copy link
Member

@ravicious ravicious left a comment

Choose a reason for hiding this comment

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

Sorry for the misunderstanding wrt location, I should've been more clear. I think we should revert to the previous state with filtering before save, I only meant that location doesn't have to be dealt with on every update.

documents: Document[],
location: DocumentUri
): DocumentUri {
return documents.find(d => d.uri === location) ? location : documents[0]!.uri;
Copy link
Member

Choose a reason for hiding this comment

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

Nit, but I think we should avoid TypeScript's ! where possible. It's not much different than using as any. It's fine in rare cases where a 3rd-party library makes some guarantees that for some reason are not expressed by types.

But here in our code, if our heart was really in it, we'd use a non-empty array to guarantee that documents always have at least one element. I don't think people do it that often in TS because the ergonomics of it are much worse compared to doing the same in Elm or Haskell.

So in this particular scenario, I think it might be better to do documents[0]?.uri rather than keep our fingers crossed that this won't ever be empty. On line 425, we already made so that an empty array is used if persistedWorkspace?.documents doesn't return anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it's not really needed actually, it's fine for location to be undefined.

@gzdunek gzdunek requested a review from ravicious December 5, 2024 16:05
>
enroll your device
</a>
. Then log out of the app, log back in, and try again.
Copy link
Member

Choose a reason for hiding this comment

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

Forgot to mention it last time, but this should be more specific – to a regular user, "the app" might mean Connect or Web UI. So perhaps "Then log out of Teleport Connect"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great idea, thanks.

@gzdunek gzdunek enabled auto-merge December 9, 2024 11:34
@gzdunek gzdunek added this pull request to the merge queue Dec 9, 2024
Merged via the queue into master with commit e2f3daf Dec 9, 2024
44 checks passed
@gzdunek gzdunek deleted the gzdunek/authorize-web-session-document branch December 9, 2024 12:11
@public-teleport-github-review-bot

@gzdunek See the table below for backport results.

Branch Result
branch/v16 Failed
branch/v17 Create PR

gzdunek added a commit that referenced this pull request Dec 9, 2024
…ired certs (#49754)

* Add `AddMetadataToRetryableError` to `AuthenticateWebDevice`

* Add a document for web session authorization

* Replace `AuthenticateWebDevice` modal with the document

* Remove dot from anchor

* Move more logic to `authorizeAndCloseDocument`

* Add a comment about `canAuthorize`

* Filter out documents before restoring session

* Add a comment about `processedRedirectUri`

* Restore filtering out documents before saving them on disk

* Fix test

* Refer to Teleport Connect instead of 'the app'

(cherry picked from commit e2f3daf)
github-merge-queue bot pushed a commit that referenced this pull request Dec 9, 2024
…le expired certs (#49932)

* Convert dialog to authorize web session to regular tab and handle expired certs (#49754)

* Add `AddMetadataToRetryableError` to `AuthenticateWebDevice`

* Add a document for web session authorization

* Replace `AuthenticateWebDevice` modal with the document

* Remove dot from anchor

* Move more logic to `authorizeAndCloseDocument`

* Add a comment about `canAuthorize`

* Filter out documents before restoring session

* Add a comment about `processedRedirectUri`

* Restore filtering out documents before saving them on disk

* Fix test

* Refer to Teleport Connect instead of 'the app'

(cherry picked from commit e2f3daf)

* Adjust alerts to the old `Alert` component
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Teleport Connect should offer reauthentication option for confirming device trust for web
3 participants