Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func (c *Client) dialGRPC(ctx context.Context, addr string) error {
otelUnaryClientInterceptor(),
metadata.UnaryClientInterceptor,
interceptors.GRPCClientUnaryErrorInterceptor,
interceptors.WithMFAUnaryInterceptor(c.performMFACeremony),
interceptors.WithMFAUnaryInterceptor(c.performAdminActionMFACeremony),
breaker.UnaryClientInterceptor(cb),
),
grpc.WithChainStreamInterceptor(
Expand Down
8 changes: 5 additions & 3 deletions api/client/mfa.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@ import (

"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/mfa"
webauthnpb "github.com/gravitational/teleport/api/types/webauthn"
)

// performMFACeremony retrieves an MFA challenge from the server, prompts the
// user to answer the challenge, and returns the resulting MFA response.
func (c *Client) performMFACeremony(ctx context.Context, promptOpts ...mfa.PromptOpt) (*proto.MFAAuthenticateResponse, error) {
// performAdminActionMFACeremony retrieves an MFA challenge from the server,
// prompts the user to answer the challenge, and returns the resulting MFA response.
func (c *Client) performAdminActionMFACeremony(ctx context.Context, promptOpts ...mfa.PromptOpt) (*proto.MFAAuthenticateResponse, error) {
if c.c.MFAPromptConstructor == nil {
return nil, trace.BadParameter("missing PromptAdminRequestMFA field, client cannot perform MFA ceremony")
}

chal, err := c.CreateAuthenticateChallenge(ctx, &proto.CreateAuthenticateChallengeRequest{
Request: &proto.CreateAuthenticateChallengeRequest_ContextUser{},
Scope: webauthnpb.ChallengeScope_CHALLENGE_SCOPE_ADMIN_ACTION,
})
if err != nil {
return nil, trace.Wrap(err)
Expand Down
2 changes: 1 addition & 1 deletion api/client/mfa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestPerformMFACeremony(t *testing.T) {
clt, err := New(ctx, cfg)
require.NoError(t, err)

resp, err := clt.performMFACeremony(ctx)
resp, err := clt.performAdminActionMFACeremony(ctx)
require.NoError(t, err)
require.Equal(t, mfaTestResp.Response, resp.Response)
}
Expand Down
1,903 changes: 1,036 additions & 867 deletions api/client/proto/authservice.pb.go

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions api/proto/teleport/legacy/client/proto/authservice.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,15 @@ message MFAAuthenticateChallenge {
// communications, in case of streaming RPCs. It may also return empty
// challenges for all other fields.
MFARequired MFARequired = 4;
// Scope is an authorization scope for this MFA challenge.
// Required. Only applies to webauthn challenges.
webauthn.ChallengeScope Scope = 5 [(gogoproto.jsontag) = "scope,omitempty"];
// AllowReuse means webauthn credentials resolved from this challenge can be
// reused for a short span of time before the challenge expires.
//
// Reuse is only permitted for specific actions by the discretion of the server.
// See the server implementation for details.
bool AllowReuse = 6 [(gogoproto.jsontag) = "allow_reuse,omitempty"];
}

// MFAAuthenticateResponse is a response to MFAAuthenticateChallenge using one
Expand Down Expand Up @@ -1841,6 +1850,15 @@ message CreateAuthenticateChallengeRequest {
// If you are issuing challenges from the root cluster, but accessing a leaf,
// call [AuthService.IsMFARequired] in the leaf instead of setting this field.
IsMFARequiredRequest MFARequiredCheck = 5 [(gogoproto.jsontag) = "mfa_required_check,omitempty"];
// Scope is an authorization scope for this MFA challenge.
// Required. Only applies to webauthn challenges.
webauthn.ChallengeScope Scope = 6 [(gogoproto.jsontag) = "scope,omitempty"];
// AllowReuse means webauthn credentials resolved from this challenge can be
// reused for a short span of time before the challenge expires.
//
// Reuse is only permitted for specific actions by the discretion of the server.
// See the server implementation for details.
bool AllowReuse = 7 [(gogoproto.jsontag) = "allow_reuse,omitempty"];
}

// CreatePrivilegeTokenRequest defines a request to obtain a privilege token.
Expand Down
30 changes: 30 additions & 0 deletions api/proto/teleport/legacy/types/webauthn/webauthn.proto
Comment thread
Joerger marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,36 @@ message SessionData {
// "required".
// An empty value is treated equivalently to "discouraged".
string user_verification = 5 [(gogoproto.jsontag) = "userVerification,omitempty"];
// Scope authorized by this webauthn session.
ChallengeScope scope = 6 [(gogoproto.jsontag) = "scope,omitempty"];
Comment thread
Joerger marked this conversation as resolved.
Comment thread
Joerger marked this conversation as resolved.
// AllowReuse indicates that this session can be used multiple times for
// authentication, until the session expires.
bool allow_reuse = 7 [(gogoproto.jsontag) = "allow_reuse,omitempty"];
Comment thread
Joerger marked this conversation as resolved.
}

// ChallengeScope is a scope authorized by a webauthn challenge's resolution.
enum ChallengeScope {
// Invalid, scope should always be specified.
CHALLENGE_SCOPE_UNSPECIFIED = 0;
Comment thread
Joerger marked this conversation as resolved.
// Standard webauthn login.
CHALLENGE_SCOPE_LOGIN = 1;
// Passwordless webauthn login.
CHALLENGE_SCOPE_PASSWORDLESS_LOGIN = 2;
// MFA device management.
CHALLENGE_SCOPE_MANAGE_DEVICES = 3;
// Account recovery.
CHALLENGE_SCOPE_RECOVERY = 4;
Comment thread
Joerger marked this conversation as resolved.
// Used for per-session MFA and moderated session presence checks.
CHALLENGE_SCOPE_SESSION = 5;
Comment thread
Joerger marked this conversation as resolved.
// Headless login approval.
CHALLENGE_SCOPE_HEADLESS = 6;
Comment thread
Joerger marked this conversation as resolved.
// Used for various administrative actions, such as adding, updating, or
// deleting administrative resources (users, roles, etc.).
//
// Note: this scope should not be used for new MFA capabilities that have
// more precise scope. Instead, new scopes should be added. This scope may
// also be split into multiple smaller scopes in the future.
CHALLENGE_SCOPE_ADMIN_ACTION = 7;
}

// User represents a WebAuthn user.
Expand Down
Loading