Skip to content
Merged
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
4 changes: 2 additions & 2 deletions lib/client/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3884,7 +3884,7 @@ func (tc *TeleportClient) directLoginWeb(ctx context.Context, secondFactorType c
}

// authenticate via the web api
clt, session, err := SSHAgentLoginWeb(ctx, SSHLoginDirect{
clt, session, err := sshAgentLoginWeb(ctx, SSHLoginDirect{
SSHLogin: sshLogin,
User: tc.Username,
Password: password,
Expand All @@ -3905,7 +3905,7 @@ func (tc *TeleportClient) mfaLocalLoginWeb(ctx context.Context, keyRing *KeyRing
return nil, nil, trace.Wrap(err)
}

clt, session, err := SSHAgentMFAWebSessionLogin(ctx, SSHLoginMFA{
clt, session, err := sshAgentMFAWebSessionLogin(ctx, SSHLoginMFA{
SSHLogin: sshLogin,
User: tc.Username,
Password: password,
Expand Down
62 changes: 42 additions & 20 deletions lib/client/weblogin.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,13 +826,8 @@ type CreateWebSessionResponse struct {
SessionInactiveTimeoutMS int `json:"sessionInactiveTimeout"`
}

// SSHAgentLoginWeb is used by tsh to fetch local user credentials via the web api.
func SSHAgentLoginWeb(ctx context.Context, login SSHLoginDirect) (*WebClient, types.WebSession, error) {
clt, _, err := initClient(login.ProxyAddr, login.Insecure, login.Pool, login.ExtraHeaders)
if err != nil {
return nil, nil, trace.Wrap(err)
}

// sshAgentLoginWebCreateSession takes an existing client and login details and attempts to create a web session using OTP token
func sshAgentLoginWebCreateSession(ctx context.Context, clt *WebClient, login SSHLoginDirect) (types.WebSession, error) {
resp, err := httplib.ConvertResponse(clt.RoundTrip(func() (*http.Response, error) {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(&CreateWebSessionReq{
Expand All @@ -852,21 +847,36 @@ func SSHAgentLoginWeb(ctx context.Context, login SSHLoginDirect) (*WebClient, ty
return clt.HTTPClient().Do(req)
}))
if err != nil {
return nil, nil, trace.Wrap(err)
return nil, trace.Wrap(err)
}

session, err := GetSessionFromResponse(resp)
if err != nil {
return nil, trace.Wrap(err)
}

return session, nil
}

// sshAgentLoginWeb is used by tsh to fetch local user credentials via the web api.
func sshAgentLoginWeb(ctx context.Context, login SSHLoginDirect) (*WebClient, types.WebSession, error) {
clt, _, err := initClient(login.ProxyAddr, login.Insecure, login.Pool, login.ExtraHeaders)
if err != nil {
return nil, nil, trace.Wrap(err)
}

session, err := sshAgentLoginWebCreateSession(ctx, clt, login)
if err != nil {
return nil, nil, trace.Wrap(err)
}

return clt, session, nil
}

// SSHAgentMFAWebSessionLogin requests a MFA challenge via the proxy web api.
// sshAgentMFAWebSessionLogin requests a MFA challenge via the proxy web api.
// If the credentials are valid, the proxy will return a challenge. We then
// prompt the user to provide 2nd factor and pass the response to the proxy.
func SSHAgentMFAWebSessionLogin(ctx context.Context, login SSHLoginMFA) (*WebClient, types.WebSession, error) {
func sshAgentMFAWebSessionLogin(ctx context.Context, login SSHLoginMFA) (*WebClient, types.WebSession, error) {
clt, _, err := initClient(login.ProxyAddr, login.Insecure, login.Pool, login.ExtraHeaders)
if err != nil {
return nil, nil, trace.Wrap(err)
Expand All @@ -880,23 +890,35 @@ func SSHAgentMFAWebSessionLogin(ctx context.Context, login SSHLoginMFA) (*WebCli
challengeResp := AuthenticateWebUserRequest{
User: login.User,
}

var session types.WebSession
// Convert back from auth gRPC proto response.
switch r := mfaResp.Response.(type) {
case *proto.MFAAuthenticateResponse_TOTP:
// If TOTP is configured we have to fallback on direct login
session, err = sshAgentLoginWebCreateSession(ctx, clt, SSHLoginDirect{
User: login.User,
Password: login.Password,
OTPToken: r.TOTP.Code,
})
if err != nil {
return nil, nil, trace.Wrap(err)
}
case *proto.MFAAuthenticateResponse_Webauthn:
challengeResp.WebauthnAssertionResponse = wantypes.CredentialAssertionResponseFromProto(r.Webauthn)
default:
// No challenge was sent, so we send back just username/password.
}
loginRespJSON, err := clt.PostJSON(ctx, clt.Endpoint("webapi", "mfa", "login", "finishsession"), challengeResp)
if err != nil {
return nil, nil, trace.Wrap(err)
}

loginRespJSON, err := clt.PostJSON(ctx, clt.Endpoint("webapi", "mfa", "login", "finishsession"), challengeResp)
if err != nil {
return nil, nil, trace.Wrap(err)
session, err = GetSessionFromResponse(loginRespJSON)
if err != nil {
return nil, nil, trace.Wrap(err)
}
default:
return nil, nil, trace.NotImplemented("unsupported MFA challenge for web session login (%T)", r)
}

session, err := GetSessionFromResponse(loginRespJSON)
if err != nil {
return nil, nil, trace.Wrap(err)
}
return clt, session, nil
}

Expand Down
Loading