EdgexAgent/device-ble-go/vendor/github.com/openziti/sdk-golang/edge-apis/authwrapper.go
2025-07-10 20:40:32 +08:00

881 lines
26 KiB
Go

// Package edge_apis_2 edge_apis_2 provides a wrapper around the generated Edge Client and Management APIs improve ease
// of use.
package edge_apis
import (
"encoding/json"
"fmt"
"github.com/go-openapi/runtime"
"github.com/go-openapi/strfmt"
"github.com/go-resty/resty/v2"
"github.com/golang-jwt/jwt/v5"
"github.com/openziti/edge-api/rest_client_api_client"
clientAuth "github.com/openziti/edge-api/rest_client_api_client/authentication"
clientControllers "github.com/openziti/edge-api/rest_client_api_client/controllers"
clientApiSession "github.com/openziti/edge-api/rest_client_api_client/current_api_session"
clientInfo "github.com/openziti/edge-api/rest_client_api_client/informational"
"github.com/openziti/edge-api/rest_management_api_client"
manAuth "github.com/openziti/edge-api/rest_management_api_client/authentication"
manControllers "github.com/openziti/edge-api/rest_management_api_client/controllers"
manCurApiSession "github.com/openziti/edge-api/rest_management_api_client/current_api_session"
manInfo "github.com/openziti/edge-api/rest_management_api_client/informational"
"github.com/openziti/edge-api/rest_model"
"github.com/openziti/edge-api/rest_util"
"github.com/openziti/foundation/v2/errorz"
"github.com/openziti/foundation/v2/stringz"
"github.com/pkg/errors"
"github.com/zitadel/oidc/v2/pkg/client/tokenexchange"
"github.com/zitadel/oidc/v2/pkg/oidc"
"golang.org/x/oauth2"
"net/http"
"net/url"
"sync"
"time"
)
const (
AuthRequestIdHeader = "auth-request-id"
TotpRequiredHeader = "totp-required"
)
// AuthEnabledApi is used as a sentinel interface to detect APIs that support authentication and to work around a golang
// limitation dealing with accessing field of generically typed fields.
type AuthEnabledApi interface {
//Authenticate will attempt to issue an authentication request using the provided credentials and http client.
//These functions act as abstraction around the underlying go-swagger generated client and will use the default
//http client if not provided.
Authenticate(credentials Credentials, configTypes []string, httpClient *http.Client) (ApiSession, error)
SetUseOidc(bool)
ListControllers() (*rest_model.ControllersList, error)
GetClientTransportPool() ClientTransportPool
SetClientTransportPool(ClientTransportPool)
}
type ApiSession interface {
//GetAccessHeader returns the HTTP header name and value that should be used to represent this ApiSession
GetAccessHeader() (string, string)
//AuthenticateRequest fulfills the interface defined by the OpenAPI libraries to authenticate client HTTP requests
AuthenticateRequest(request runtime.ClientRequest, _ strfmt.Registry) error
//GetToken returns the ApiSessions' token bytes
GetToken() []byte
//GetExpiresAt returns the time when the ApiSession will expire.
GetExpiresAt() *time.Time
//GetAuthQueries returns a list of authentication queries the ApiSession is subjected to
GetAuthQueries() rest_model.AuthQueryList
//GetIdentityName returns the name of the authenticating identity
GetIdentityName() string
//GetIdentityId returns the id of the authenticating identity
GetIdentityId() string
//GetId returns the id of the ApiSession
GetId() string
//RequiresRouterTokenUpdate returns true if the token is a bearer token requires updating on edge router connections.
RequiresRouterTokenUpdate() bool
GetRequestHeaders() http.Header
}
var _ ApiSession = (*ApiSessionLegacy)(nil)
var _ ApiSession = (*ApiSessionOidc)(nil)
// ApiSessionLegacy represents OpenZiti's original authentication API Session Detail, supplied in the `zt-session` header.
// It has been supplanted by OIDC authentication represented by ApiSessionOidc.
type ApiSessionLegacy struct {
Detail *rest_model.CurrentAPISessionDetail
RequestHeaders http.Header
}
func (a *ApiSessionLegacy) GetRequestHeaders() http.Header {
return a.RequestHeaders
}
func (a *ApiSessionLegacy) RequiresRouterTokenUpdate() bool {
return false
}
func (a *ApiSessionLegacy) GetId() string {
return stringz.OrEmpty(a.Detail.ID)
}
func (a *ApiSessionLegacy) GetIdentityName() string {
return a.Detail.Identity.Name
}
func (a *ApiSessionLegacy) GetIdentityId() string {
return stringz.OrEmpty(a.Detail.IdentityID)
}
// GetAccessHeader returns the header and header token value should be used for authentication requests
func (a *ApiSessionLegacy) GetAccessHeader() (string, string) {
if a.Detail != nil && a.Detail.Token != nil {
return "zt-session", *a.Detail.Token
}
return "", ""
}
func (a *ApiSessionLegacy) AuthenticateRequest(request runtime.ClientRequest, _ strfmt.Registry) error {
if a == nil {
return errors.New("api session is nil")
}
for h, v := range a.RequestHeaders {
err := request.SetHeaderParam(h, v...)
if err != nil {
return err
}
}
//legacy does not support multiple zt-session headers, so we can it sfely
header, val := a.GetAccessHeader()
err := request.SetHeaderParam(header, val)
if err != nil {
return err
}
return nil
}
func (a *ApiSessionLegacy) GetToken() []byte {
if a.Detail != nil && a.Detail.Token != nil {
return []byte(*a.Detail.Token)
}
return nil
}
func (a *ApiSessionLegacy) GetAuthQueries() rest_model.AuthQueryList {
return a.Detail.AuthQueries
}
func (a *ApiSessionLegacy) GetExpiresAt() *time.Time {
if a.Detail != nil {
return (*time.Time)(a.Detail.ExpiresAt)
}
return nil
}
// ApiSessionOidc represents an authenticated session backed by OIDC tokens.
type ApiSessionOidc struct {
OidcTokens *oidc.Tokens[*oidc.IDTokenClaims]
RequestHeaders http.Header
}
func (a *ApiSessionOidc) GetRequestHeaders() http.Header {
return a.RequestHeaders
}
func (a *ApiSessionOidc) RequiresRouterTokenUpdate() bool {
return true
}
func (a *ApiSessionOidc) GetAccessClaims() (*ApiAccessClaims, error) {
claims := &ApiAccessClaims{}
parser := jwt.NewParser()
_, _, err := parser.ParseUnverified(a.OidcTokens.AccessToken, claims)
if err != nil {
return nil, err
}
return claims, nil
}
func (a *ApiSessionOidc) GetId() string {
claims, err := a.GetAccessClaims()
if err != nil {
return ""
}
return claims.ApiSessionId
}
func (a *ApiSessionOidc) GetIdentityName() string {
return a.OidcTokens.IDTokenClaims.Name
}
func (a *ApiSessionOidc) GetIdentityId() string {
return a.OidcTokens.IDTokenClaims.Subject
}
// GetAccessHeader returns the header and header token value should be used for authentication requests
func (a *ApiSessionOidc) GetAccessHeader() (string, string) {
if a.OidcTokens != nil {
return "authorization", "Bearer " + a.OidcTokens.AccessToken
}
return "", ""
}
func (a *ApiSessionOidc) AuthenticateRequest(request runtime.ClientRequest, _ strfmt.Registry) error {
if a == nil {
return errors.New("api session is nil")
}
if a.RequestHeaders == nil {
a.RequestHeaders = http.Header{}
}
//multiple Authorization headers are allowed, obtain all auth header candidates
primaryAuthHeader, primaryAuthValue := a.GetAccessHeader()
altAuthValues := a.RequestHeaders.Get(primaryAuthHeader)
authValues := []string{primaryAuthValue}
if len(altAuthValues) > 0 {
authValues = append(authValues, altAuthValues)
}
//set request headers
for h, v := range a.RequestHeaders {
err := request.SetHeaderParam(h, v...)
if err != nil {
return err
}
}
//restore auth headers
err := request.SetHeaderParam(primaryAuthHeader, authValues...)
if err != nil {
return err
}
return nil
}
func (a *ApiSessionOidc) GetToken() []byte {
if a.OidcTokens != nil && a.OidcTokens.AccessToken != "" {
return []byte(a.OidcTokens.AccessToken)
}
return nil
}
func (a *ApiSessionOidc) GetAuthQueries() rest_model.AuthQueryList {
//todo convert JWT auth queries to rest_model.AuthQueryList
return nil
}
func (a *ApiSessionOidc) GetExpiresAt() *time.Time {
if a.OidcTokens != nil {
return &a.OidcTokens.Expiry
}
return nil
}
var _ AuthEnabledApi = (*ZitiEdgeManagement)(nil)
// ZitiEdgeManagement is an alias of the go-swagger generated client that allows this package to add additional
// functionality to the alias type to implement the AuthEnabledApi interface.
type ZitiEdgeManagement struct {
*rest_management_api_client.ZitiEdgeManagement
// useOidc tracks if OIDC auth should be used
useOidc bool
// useOidcExplicitlySet signals if useOidc was set from an external caller and should be used as is
useOidcExplicitlySet bool
// oidcDynamicallyEnabled will cause the client to check the controller for OIDC support and use if possible as long as useOidc was not explicitly set
oidcDynamicallyEnabled bool //currently defaults false till HA release
versionOnce sync.Once
versionInfo *rest_model.Version
TotpCallback func(chan string)
ClientTransportPool ClientTransportPool
}
func (self *ZitiEdgeManagement) SetClientTransportPool(transportPool ClientTransportPool) {
self.ClientTransportPool = transportPool
}
func (self *ZitiEdgeManagement) GetClientTransportPool() ClientTransportPool {
return self.ClientTransportPool
}
func (self *ZitiEdgeManagement) ListControllers() (*rest_model.ControllersList, error) {
params := manControllers.NewListControllersParams()
resp, err := self.Controllers.ListControllers(params, nil)
if err != nil {
return nil, err
}
return &resp.GetPayload().Data, nil
}
func (self *ZitiEdgeManagement) Authenticate(credentials Credentials, configTypes []string, httpClient *http.Client) (ApiSession, error) {
self.versionOnce.Do(func() {
if self.useOidcExplicitlySet {
return
}
if self.oidcDynamicallyEnabled {
versionParams := manInfo.NewListVersionParams()
versionResp, _ := self.Informational.ListVersion(versionParams)
if versionResp != nil {
self.versionInfo = versionResp.Payload.Data
self.useOidc = stringz.Contains(self.versionInfo.Capabilities, string(rest_model.CapabilitiesOIDCAUTH))
}
} else {
self.useOidc = false
}
})
if self.useOidc {
return self.oidcAuth(credentials, configTypes, httpClient)
}
return self.legacyAuth(credentials, configTypes, httpClient)
}
func (self *ZitiEdgeManagement) legacyAuth(credentials Credentials, configTypes []string, httpClient *http.Client) (ApiSession, error) {
params := manAuth.NewAuthenticateParams()
params.Auth = credentials.Payload()
params.Method = credentials.Method()
params.Auth.ConfigTypes = append(params.Auth.ConfigTypes, configTypes...)
certs := credentials.TlsCerts()
if len(certs) != 0 {
if transport, ok := httpClient.Transport.(*http.Transport); ok {
transport.TLSClientConfig.Certificates = certs
transport.CloseIdleConnections()
}
}
resp, err := self.Authentication.Authenticate(params, getClientAuthInfoOp(credentials, httpClient))
if err != nil {
return nil, err
}
return &ApiSessionLegacy{
Detail: resp.GetPayload().Data,
RequestHeaders: credentials.GetRequestHeaders()}, err
}
func (self *ZitiEdgeManagement) oidcAuth(credentials Credentials, configTypeOverrides []string, httpClient *http.Client) (ApiSession, error) {
return oidcAuth(self.ClientTransportPool, credentials, configTypeOverrides, httpClient, self.TotpCallback)
}
func (self *ZitiEdgeManagement) SetUseOidc(use bool) {
self.useOidcExplicitlySet = true
self.useOidc = use
}
func (self *ZitiEdgeManagement) SetAllowOidcDynamicallyEnabled(allow bool) {
self.oidcDynamicallyEnabled = allow
}
func (self *ZitiEdgeManagement) RefreshApiSession(apiSession ApiSession, httpClient *http.Client) (ApiSession, error) {
switch s := apiSession.(type) {
case *ApiSessionLegacy:
params := manCurApiSession.NewGetCurrentAPISessionParams()
_, err := self.CurrentAPISession.GetCurrentAPISession(params, s)
if err != nil {
return nil, rest_util.WrapErr(err)
}
return s, nil
case *ApiSessionOidc:
tokens, err := self.ExchangeTokens(s.OidcTokens, httpClient)
if err != nil {
return nil, err
}
return &ApiSessionOidc{
OidcTokens: tokens,
RequestHeaders: apiSession.GetRequestHeaders(),
}, nil
}
return nil, errors.New("api session does not have any tokens")
}
func (self *ZitiEdgeManagement) ExchangeTokens(curTokens *oidc.Tokens[*oidc.IDTokenClaims], httpClient *http.Client) (*oidc.Tokens[*oidc.IDTokenClaims], error) {
return exchangeTokens(self.ClientTransportPool, curTokens, httpClient)
}
var _ AuthEnabledApi = (*ZitiEdgeClient)(nil)
// ZitiEdgeClient is an alias of the go-swagger generated client that allows this package to add additional
// functionality to the alias type to implement the AuthEnabledApi interface.
type ZitiEdgeClient struct {
*rest_client_api_client.ZitiEdgeClient
// useOidc tracks if OIDC auth should be used
useOidc bool
// useOidcExplicitlySet signals if useOidc was set from an external caller and should be used as is
useOidcExplicitlySet bool
// oidcDynamicallyEnabled will cause the client to check the controller for OIDC support and use if possible as long as useOidc was not explicitly set.
oidcDynamicallyEnabled bool //currently defaults false till HA release
versionInfo *rest_model.Version
versionOnce sync.Once
TotpCallback func(chan string)
ClientTransportPool ClientTransportPool
}
func (self *ZitiEdgeClient) GetClientTransportPool() ClientTransportPool {
return self.ClientTransportPool
}
func (self *ZitiEdgeClient) SetClientTransportPool(transportPool ClientTransportPool) {
self.ClientTransportPool = transportPool
}
func (self *ZitiEdgeClient) ListControllers() (*rest_model.ControllersList, error) {
params := clientControllers.NewListControllersParams()
resp, err := self.Controllers.ListControllers(params, nil)
if err != nil {
return nil, err
}
return &resp.GetPayload().Data, nil
}
func (self *ZitiEdgeClient) Authenticate(credentials Credentials, configTypesOverrides []string, httpClient *http.Client) (ApiSession, error) {
self.versionOnce.Do(func() {
if self.useOidcExplicitlySet {
return
}
if self.oidcDynamicallyEnabled {
versionParams := clientInfo.NewListVersionParams()
versionResp, _ := self.Informational.ListVersion(versionParams)
if versionResp != nil {
self.versionInfo = versionResp.Payload.Data
self.useOidc = stringz.Contains(self.versionInfo.Capabilities, string(rest_model.CapabilitiesOIDCAUTH))
}
} else {
self.useOidc = false
}
})
if self.useOidc {
return self.oidcAuth(credentials, configTypesOverrides, httpClient)
}
return self.legacyAuth(credentials, configTypesOverrides, httpClient)
}
func (self *ZitiEdgeClient) legacyAuth(credentials Credentials, configTypes []string, httpClient *http.Client) (ApiSession, error) {
params := clientAuth.NewAuthenticateParams()
params.Auth = credentials.Payload()
params.Method = credentials.Method()
params.Auth.ConfigTypes = append(params.Auth.ConfigTypes, configTypes...)
certs := credentials.TlsCerts()
if len(certs) != 0 {
if transport, ok := httpClient.Transport.(*http.Transport); ok {
transport.TLSClientConfig.Certificates = certs
transport.CloseIdleConnections()
}
}
resp, err := self.Authentication.Authenticate(params, getClientAuthInfoOp(credentials, httpClient))
if err != nil {
return nil, err
}
return &ApiSessionLegacy{Detail: resp.GetPayload().Data, RequestHeaders: credentials.GetRequestHeaders()}, err
}
func (self *ZitiEdgeClient) oidcAuth(credentials Credentials, configTypeOverrides []string, httpClient *http.Client) (ApiSession, error) {
return oidcAuth(self.ClientTransportPool, credentials, configTypeOverrides, httpClient, self.TotpCallback)
}
func (self *ZitiEdgeClient) SetUseOidc(use bool) {
self.useOidcExplicitlySet = true
self.useOidc = use
}
func (self *ZitiEdgeClient) SetAllowOidcDynamicallyEnabled(allow bool) {
self.oidcDynamicallyEnabled = allow
}
func (self *ZitiEdgeClient) RefreshApiSession(apiSession ApiSession, httpClient *http.Client) (ApiSession, error) {
switch s := apiSession.(type) {
case *ApiSessionLegacy:
params := clientApiSession.NewGetCurrentAPISessionParams()
newApiSessionDetail, err := self.CurrentAPISession.GetCurrentAPISession(params, s)
if err != nil {
return nil, rest_util.WrapErr(err)
}
newApiSession := &ApiSessionLegacy{
Detail: newApiSessionDetail.Payload.Data,
RequestHeaders: apiSession.GetRequestHeaders(),
}
return newApiSession, nil
case *ApiSessionOidc:
tokens, err := self.ExchangeTokens(s.OidcTokens, httpClient)
if err != nil {
return nil, err
}
return &ApiSessionOidc{
OidcTokens: tokens,
RequestHeaders: apiSession.GetRequestHeaders(),
}, nil
}
return nil, errors.New("api session does not have any tokens")
}
func (self *ZitiEdgeClient) ExchangeTokens(curTokens *oidc.Tokens[*oidc.IDTokenClaims], httpClient *http.Client) (*oidc.Tokens[*oidc.IDTokenClaims], error) {
return exchangeTokens(self.ClientTransportPool, curTokens, httpClient)
}
func exchangeTokens(clientTransportPool ClientTransportPool, curTokens *oidc.Tokens[*oidc.IDTokenClaims], client *http.Client) (*oidc.Tokens[*oidc.IDTokenClaims], error) {
subjectToken := curTokens.RefreshToken
subjectTokenType := oidc.RefreshTokenType
// if subjectToken is "", then we don't have a refresh token, attempt to exchange a non-expired access token
if subjectToken == "" {
if curTokens.Expiry.Before(time.Now()) {
return nil, errors.New("cannot exchange token: refresh token not found, access token expired")
}
if curTokens.AccessToken == "" {
return nil, errors.New("cannot exchange token: refresh token not found, access token not found")
}
subjectToken = curTokens.AccessToken
subjectTokenType = oidc.AccessTokenType
}
var outTokens *oidc.Tokens[*oidc.IDTokenClaims]
_, err := clientTransportPool.TryTransportForF(func(transport *ApiClientTransport) (any, error) {
apiHost := transport.ApiUrl.Host
issuer := "https://" + apiHost + "/oidc"
tokenEndpoint := "https://" + apiHost + "/oidc/oauth/token"
te, err := tokenexchange.NewTokenExchangerClientCredentials(issuer, "native", "", tokenexchange.WithHTTPClient(client), tokenexchange.WithStaticTokenEndpoint(issuer, tokenEndpoint))
if err != nil {
return nil, err
}
var tokenResponse *oidc.TokenExchangeResponse
now := time.Now()
switch subjectTokenType {
case oidc.RefreshTokenType:
tokenResponse, err = tokenexchange.ExchangeToken(te, subjectToken, subjectTokenType, "", "", nil, nil, nil, oidc.RefreshTokenType)
case oidc.AccessTokenType:
tokenResponse, err = tokenexchange.ExchangeToken(te, subjectToken, subjectTokenType, "", "", nil, nil, nil, oidc.AccessTokenType)
}
if err != nil {
return nil, err
}
idResp, err := tokenexchange.ExchangeToken(te, subjectToken, subjectTokenType, "", "", nil, nil, nil, oidc.IDTokenType)
if err != nil {
return nil, err
}
idClaims := &IdClaims{}
//access token is used to hold id token per zitadel comments
_, _, err = jwt.NewParser().ParseUnverified(idResp.AccessToken, idClaims)
if err != nil {
return nil, err
}
outTokens = &oidc.Tokens[*oidc.IDTokenClaims]{
Token: &oauth2.Token{
AccessToken: tokenResponse.AccessToken,
TokenType: tokenResponse.TokenType,
RefreshToken: tokenResponse.RefreshToken,
Expiry: now.Add(time.Second * time.Duration(tokenResponse.ExpiresIn)),
},
IDTokenClaims: &idClaims.IDTokenClaims,
IDToken: idResp.AccessToken, //access token field is used to hold id token per zitadel comments
}
return outTokens, nil
})
if err != nil {
return nil, err
}
return outTokens, nil
}
type authPayload struct {
*rest_model.Authenticate
AuthRequestId string `json:"id"`
}
type totpCodePayload struct {
rest_model.MfaCode
AuthRequestId string `json:"id"`
}
func (a *authPayload) toValues() url.Values {
result := url.Values{
"id": []string{a.AuthRequestId},
"password": []string{string(a.Password)},
"username": []string{string(a.Username)},
"configTypes": a.ConfigTypes,
"envArch": []string{a.EnvInfo.Arch},
"envOs": []string{a.EnvInfo.Os},
"envOsRelease": []string{a.EnvInfo.OsRelease},
"envOsVersion": []string{a.EnvInfo.OsVersion},
"sdkAppID": []string{a.SdkInfo.AppID},
"sdkAppVersion": []string{a.SdkInfo.AppVersion},
"sdkBranch": []string{a.SdkInfo.Branch},
"sdkRevision": []string{a.SdkInfo.Revision},
"sdkType": []string{a.SdkInfo.Type},
"sdkVersion": []string{a.SdkInfo.Version},
}
return result
}
func oidcAuth(clientTransportPool ClientTransportPool, credentials Credentials, configTypeOverrides []string, httpClient *http.Client, totpCallback func(chan string)) (ApiSession, error) {
payload := &authPayload{
Authenticate: credentials.Payload(),
}
method := credentials.Method()
if configTypeOverrides != nil {
payload.ConfigTypes = configTypeOverrides
}
certs := credentials.TlsCerts()
if len(certs) != 0 {
if transport, ok := httpClient.Transport.(*http.Transport); ok {
transport.TLSClientConfig.Certificates = certs
transport.CloseIdleConnections()
}
}
var outTokens *oidc.Tokens[*oidc.IDTokenClaims]
_, err := clientTransportPool.TryTransportForF(func(transport *ApiClientTransport) (any, error) {
rpServer, err := newLocalRpServer(transport.ApiUrl.Host, method)
if err != nil {
return nil, err
}
rpServer.Start()
defer rpServer.Stop()
client := resty.NewWithClient(httpClient)
apiHost := transport.ApiUrl.Hostname()
client.SetRedirectPolicy(resty.DomainCheckRedirectPolicy("127.0.0.1", "localhost", apiHost))
resp, err := client.R().Get(rpServer.LoginUri)
if err != nil {
return nil, err
}
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("local rp login response is expected to be HTTP status %d got %d with body: %s", http.StatusOK, resp.StatusCode(), resp.Body())
}
payload.AuthRequestId = resp.Header().Get(AuthRequestIdHeader)
if payload.AuthRequestId == "" {
return nil, errors.New("could not find auth request id header")
}
opLoginUri := "https://" + resp.RawResponse.Request.URL.Host + "/oidc/login/" + method
totpUri := "https://" + resp.RawResponse.Request.URL.Host + "/oidc/login/totp"
formData := payload.toValues()
req := client.R()
clientRequest := asClientRequest(req, client)
err = credentials.AuthenticateRequest(clientRequest, strfmt.Default)
if err != nil {
return nil, err
}
resp, err = req.SetFormDataFromValues(formData).Post(opLoginUri)
if err != nil {
return nil, err
}
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("remote op login response is expected to be HTTP status %d got %d with body: %s", http.StatusOK, resp.StatusCode(), resp.Body())
}
authRequestId := payload.AuthRequestId
totpRequiredHeader := resp.Header().Get(TotpRequiredHeader)
totpRequired := totpRequiredHeader != ""
totpCode := ""
if totpRequired {
if totpCallback == nil {
return nil, errors.New("totp is required but not totp callback was defined")
}
codeChan := make(chan string)
go totpCallback(codeChan)
select {
case code := <-codeChan:
totpCode = code
case <-time.After(30 * time.Minute):
return nil, fmt.Errorf("timedout waiting for totpT callback")
}
resp, err = client.R().SetBody(&totpCodePayload{
MfaCode: rest_model.MfaCode{
Code: &totpCode,
},
AuthRequestId: authRequestId,
}).Post(totpUri)
if err != nil {
return nil, err
}
if resp.StatusCode() != http.StatusOK {
apiErr := &errorz.ApiError{}
err = json.Unmarshal(resp.Body(), apiErr)
if err != nil {
return nil, fmt.Errorf("could not verify TOTP MFA code recieved %d - could not parse body: %s", resp.StatusCode(), string(resp.Body()))
}
return nil, apiErr
}
}
var tokens *oidc.Tokens[*oidc.IDTokenClaims]
select {
case tokens = <-rpServer.TokenChan:
case <-time.After(30 * time.Minute):
}
if tokens == nil {
return nil, errors.New("authentication did not complete, received nil tokens")
}
outTokens = tokens
return nil, nil
})
if err != nil {
return nil, err
}
return &ApiSessionOidc{
OidcTokens: outTokens,
RequestHeaders: credentials.GetRequestHeaders(),
}, nil
}
// restyClientRequest is meant to mimic open api's client request which is a combination
// of resty's request and client.
type restyClientRequest struct {
restyRequest *resty.Request
restyClient *resty.Client
}
func (r *restyClientRequest) SetHeaderParam(s string, s2 ...string) error {
r.restyRequest.Header[s] = s2
return nil
}
func (r *restyClientRequest) GetHeaderParams() http.Header {
return r.restyRequest.Header
}
func (r *restyClientRequest) SetQueryParam(s string, s2 ...string) error {
r.restyRequest.QueryParam[s] = s2
return nil
}
func (r *restyClientRequest) SetFormParam(s string, s2 ...string) error {
r.restyRequest.FormData[s] = s2
return nil
}
func (r *restyClientRequest) SetPathParam(s string, s2 string) error {
r.restyRequest.PathParams[s] = s2
return nil
}
func (r *restyClientRequest) GetQueryParams() url.Values {
return r.restyRequest.QueryParam
}
func (r *restyClientRequest) SetFileParam(s string, closer ...runtime.NamedReadCloser) error {
for _, curCloser := range closer {
r.restyRequest.SetFileReader(s, curCloser.Name(), curCloser)
}
return nil
}
func (r *restyClientRequest) SetBodyParam(i interface{}) error {
r.restyRequest.SetBody(i)
return nil
}
func (r *restyClientRequest) SetTimeout(duration time.Duration) error {
r.restyClient.SetTimeout(duration)
return nil
}
func (r *restyClientRequest) GetMethod() string {
return r.restyRequest.Method
}
func (r *restyClientRequest) GetPath() string {
return r.restyRequest.URL
}
func (r *restyClientRequest) GetBody() []byte {
return r.restyRequest.Body.([]byte)
}
func (r *restyClientRequest) GetBodyParam() interface{} {
return r.restyRequest.Body
}
func (r *restyClientRequest) GetFileParam() map[string][]runtime.NamedReadCloser {
return nil
}
func asClientRequest(request *resty.Request, client *resty.Client) runtime.ClientRequest {
return &restyClientRequest{request, client}
}