-
Notifications
You must be signed in to change notification settings - Fork 1.8k
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
Teleport Connect MFA prompt #34135
Teleport Connect MFA prompt #34135
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Connect implementation should work. I assume that we're going to open an important modal to ask either for a key tap or for TOTP.
The only scenario where this would not work is if we tried to execute an administrative action from within an important modal. Imagine this convoluted-on-purpose scenario where the user clicks "Change username" and it shows an important modal with an input to enter a new name. Then when they submit the form, tshd prompts for MFA using an important modal. But tshd cannot do that as there's already an important modal that is open! This is a problem similar to #31070.
Fortunately, AFAIK the only administrative actions that can be executed from Connect are access request approvals and setting up Connect My Computer (upsert role + update user + create token) and none of them use a modal.
lib/teleterm/daemon/mfaPrompt.go
Outdated
return &proto.MFAAuthenticateResponse{ | ||
Response: &proto.MFAAuthenticateResponse_TOTP{ | ||
TOTP: &proto.TOTPResponse{Code: resp.TotpCode}, | ||
}, | ||
}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The final version will have to pass Webauthn responses as well, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, it's not necessary. The flow I laid out here is:
sequenceDiagram
participant tshd as tsh daemon
participant electron as Electron app
actor user
tshd->>electron: PrompMFA req
electron->>user: show MFA modal
alt user enters otp
user->>electron: otp code
electron->>tshd: otp code
else user closes modal
user->>electron: close modal
electron->>tshd: cancel request
else user completes webauthn
user->>tshd: user taps webauthn key
tshd->>electron: close modal (no error)
end
Previously I had trouble handling tshd cancelling its request without outputting an error in the Electron App, but I'm going to try again. Otherwise we would need to add another call from Electron -> tshd to handle Webauthn, which is similar to how we handled headless. The extra steps in the headless flow made more sense whereas I think we should try to get away with the short flow here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cancel request and close modal is where my subsequent questions would be. How do you plan to implement "close modal"?
Close modal
It seems to me that it might be worthwhile to tie this logic to the request-response cycle. In that sense, closing the modal by tsh could be implemented by cancelling the request. I'm not sure what you mean by cancelling a request without outputting an error in the Electron app, but AFAIR the relogin logic already handles request cancellation.
teleport/web/packages/teleterm/src/ui/appContext.ts
Lines 172 to 178 in 44f17e7
this.subscribeToTshdEvent('relogin', ({ request, onCancelled }) => { | |
// The handler for the relogin event should return only after the relogin procedure finishes. | |
return this.reloginService.relogin( | |
request as ReloginRequest, | |
onCancelled | |
); | |
}); |
onRequestCancelled(closeDialog); |
This is hidden behind some layers of abstraction but essentially when the Go code cancels the request, grpc-js emits the cancelled
event on the call
object.
teleport/web/packages/teleterm/src/services/tshdEvents/index.ts
Lines 151 to 161 in 44f17e7
const service: apiService.ITshdEventsServiceServer = { | |
relogin: (call, callback) => { | |
const request = call.request.toObject(); | |
logger.info('Emitting relogin', request); | |
const onCancelled = (callback: () => void) => { | |
call.on('cancelled', callback); | |
}; | |
emitter.emit('relogin', { request, onCancelled }).then( |
Cancel request
Here I suppose the tshd events service could return a response with code ABORTED. I haven't done that anywhere yet, but taking the relogin service just as an example, it'd have to look something like this:
diff --git a/web/packages/teleterm/src/services/tshdEvents/index.ts b/web/packages/teleterm/src/services/tshdEvents/index.ts
index 8c98badf3e..8a1831e890 100644
--- a/web/packages/teleterm/src/services/tshdEvents/index.ts
+++ b/web/packages/teleterm/src/services/tshdEvents/index.ts
@@ -163,6 +163,18 @@ function createService(logger: Logger): {
callback(null, new api.ReloginResponse());
},
error => {
+ // error passes through the context bridge [1] so none of its properties are preserved
+ // other than the message.
+ //
+ // [1] https://www.electronjs.org/docs/latest/api/context-bridge
+ if (error?.message?.includes('aborted')) {
+ const status = new grpc.StatusBuilder()
+ .withCode(grpc.status.ABORTED)
+ .build();
+ callback(status);
+ return;
+ }
+
callback(error);
}
);
diff --git a/web/packages/teleterm/src/ui/services/relogin/reloginService.ts b/web/packages/teleterm/src/ui/services/relogin/reloginService.ts
index 06aa001018..6036f1bcd6 100644
--- a/web/packages/teleterm/src/ui/services/relogin/reloginService.ts
+++ b/web/packages/teleterm/src/ui/services/relogin/reloginService.ts
@@ -60,6 +60,8 @@ export class ReloginService {
reject(new Error('Login process was canceled by the user')),
});
+ throw new Error('aborted');
+
onRequestCancelled(closeDialog);
});
}
👍
Good point, this is something we'll need to keep an eye out for in the future. For now, I'll make it so the tshd prompt (yubikey blinking) goes through while |
dad0cf6
to
ebe3f08
Compare
The PR changelog entry failed validation: Changelog entry not found in the PR body. Please add a "no-changelog" label to the PR, or changelog lines starting with |
ebe3f08
to
938b0ee
Compare
Co-authored-by: Lisa Kim <[email protected]>
10f039a
to
b89cc01
Compare
d2e82ce
to
363472d
Compare
Add a tsh daemon MFA prompt to replace the standard CLI-based MFA prompt. By using this prompt in the daemon's
TeleportClient
s, MFA prompts like that for admin actions will issue atshdEventService
messagepromptMFA
instead of just outputting the tap prompt to Stderr. This also allows Connect to support TOTP prompts.This change is intended to be a no-op (continue using CLI-based prompt) until the client-side is implemented with a Webauth/TOTP prompt modal in a follow up PR.
Relies on #34087