Files
zt/controller/oidc_auth/requests.go
Andrew MartinezandGitHub 87c9ce3718 fix #3178 apis missing from controllers (#3180)
* fix #3178 apis missing from controllers, fixes #3193 adds totp auth query enrollment flag

- fixed controller store to no longer blank apis on all CUD actions
- controllers now report their current config state on leader change to
  avoid stale information
- fixes peer disconnect to only set online state
- adds enrollment state flags to auth queries
- adds test for OIDC MFA enrollment/recovery/delete
- adds controller store unit tests
2025-08-05 09:15:27 -04:00

272 lines
7.9 KiB
Go

/*
Copyright NetFoundry Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package oidc_auth
import (
"crypto/sha1"
"crypto/x509"
"fmt"
"github.com/openziti/edge-api/rest_model"
"github.com/openziti/foundation/v2/stringz"
"github.com/openziti/ziti/common"
"github.com/openziti/ziti/controller/model"
"time"
"github.com/zitadel/oidc/v3/pkg/oidc"
)
// AuthRequest represents an OIDC authentication request and implements op.AuthRequest
type AuthRequest struct {
oidc.AuthRequest
Id string
CreationDate time.Time
IdentityId string
AuthTime time.Time
ApiSessionId string
IsTotpEnrolled bool
SecondaryTotpRequired bool
SecondaryExtJwtSigner *model.ExternalJwtSigner
ConfigTypes []string
Amr map[string]struct{}
PeerCerts []*x509.Certificate
RequestedMethod string
BearerTokenDetected bool
SdkInfo *rest_model.SdkInfo
EnvInfo *rest_model.EnvInfo
RemoteAddress string
IsCertExtendable bool
IsCertExtendRequested bool
IsCertKeyRollRequested bool
AuthenticatorId string
ImproperClientCertChain bool
}
// GetID returns an AuthRequest's ID and implements op.AuthRequest
func (a *AuthRequest) GetID() string {
return a.Id
}
// GetACR returns the authentication class reference provided by client and implements oidc.AuthRequest
// All ACRs are currently ignored.
func (a *AuthRequest) GetACR() string {
return ""
}
// GetAMR returns the authentication method references the authentication has undergone and implements op.AuthRequest
func (a *AuthRequest) GetAMR() []string {
result := make([]string, len(a.Amr))
i := 0
for k := range a.Amr {
result[i] = k
i = i + 1
}
return result
}
// HasFullAuth returns true if an authentication request has passed all primary and secondary authentications.
func (a *AuthRequest) HasFullAuth() bool {
return a.HasPrimaryAuth() && a.HasSecondaryAuth()
}
// HasPrimaryAuth returns true if a primary authentication mechanism has been passed.
func (a *AuthRequest) HasPrimaryAuth() bool {
return a.HasAmr(AuthMethodCert) || a.HasAmr(AuthMethodPassword) || a.HasAmr(AuthMethodExtJwt)
}
// HasSecondaryAuth returns true if all applicable secondary authentications have been passed
func (a *AuthRequest) HasSecondaryAuth() bool {
return (!a.SecondaryTotpRequired || a.HasAmr(AuthMethodSecondaryTotp)) &&
(a.SecondaryExtJwtSigner == nil || a.HasAmrExtJwtId(a.SecondaryExtJwtSigner.Id))
}
// HasAmr returns true if the supplied amr is present
func (a *AuthRequest) HasAmr(amr string) bool {
_, found := a.Amr[amr]
return found
}
func (a *AuthRequest) HasAmrExtJwtId(id string) bool {
return a.HasAmr(AuthMethodSecondaryExtJwt + ":" + id)
}
// AddAmr adds the supplied amr
func (a *AuthRequest) AddAmr(amr string) {
if a.Amr == nil {
a.Amr = map[string]struct{}{}
}
a.Amr[amr] = struct{}{}
}
// GetAudience returns all current audience targets and implements op.AuthRequest
func (a *AuthRequest) GetAudience() []string {
return []string{a.ClientID}
}
// GetAuthTime returns the time at which authentication has occurred and implements op.AuthRequest
func (a *AuthRequest) GetAuthTime() time.Time {
return a.AuthTime
}
// GetClientID returns the client id requested and implements op.AuthRequest
func (a *AuthRequest) GetClientID() string {
return a.ClientID
}
// GetCodeChallenge returns the rp supplied code change and implements op.AuthRequest
func (a *AuthRequest) GetCodeChallenge() *oidc.CodeChallenge {
return &oidc.CodeChallenge{
Challenge: a.CodeChallenge,
Method: a.CodeChallengeMethod,
}
}
// GetNonce returns the rp supplied nonce and implements op.AuthRequest
func (a *AuthRequest) GetNonce() string {
return a.Nonce
}
// GetRedirectURI returns the rp supplied redirect target and implements op.AuthRequest
func (a *AuthRequest) GetRedirectURI() string {
return a.RedirectURI
}
// GetResponseType returns the rp supplied response type and implements op.AuthRequest
func (a *AuthRequest) GetResponseType() oidc.ResponseType {
return a.ResponseType
}
// GetResponseMode is not supported and all tokens are turned via query string and implements op.AuthRequest
func (a *AuthRequest) GetResponseMode() oidc.ResponseMode {
return ""
}
// GetScopes returns the current scopes and implements op.AuthRequest
func (a *AuthRequest) GetScopes() []string {
return a.Scopes
}
// GetState returns the rp provided state and implements op.AuthRequest
func (a *AuthRequest) GetState() string {
return a.State
}
// GetSubject returns the target subject and implements op.AuthRequest
func (a *AuthRequest) GetSubject() string {
return a.IdentityId
}
// Done returns true once authentication has been completed and implements op.AuthRequest
func (a *AuthRequest) Done() bool {
return a.HasFullAuth()
}
func (a *AuthRequest) GetCertFingerprints() []string {
var prints []string
for _, cert := range a.PeerCerts {
prints = append(prints, fmt.Sprintf("%x", sha1.Sum(cert.Raw)))
}
return prints
}
func (a *AuthRequest) NeedsTotp() bool {
return a.SecondaryTotpRequired && !a.HasAmr(AuthMethodSecondaryTotp)
}
func (a *AuthRequest) NeedsSecondaryExtJwt() bool {
return a.SecondaryExtJwtSigner != nil && !a.HasAmrExtJwtId(a.SecondaryExtJwtSigner.Id)
}
func (a *AuthRequest) GetAuthQueries() []*rest_model.AuthQueryDetail {
var authQueries []*rest_model.AuthQueryDetail
if a.NeedsTotp() {
provider := rest_model.MfaProvidersZiti
authQueries = append(authQueries, &rest_model.AuthQueryDetail{
Format: rest_model.MfaFormatsNumeric,
HTTPMethod: "POST",
HTTPURL: "./oidc/login/totp",
MaxLength: 8,
MinLength: 6,
Provider: &provider,
TypeID: rest_model.AuthQueryTypeTOTP,
IsTotpEnrolled: a.IsTotpEnrolled,
})
}
if a.NeedsSecondaryExtJwt() {
provider := rest_model.MfaProvidersURL
authQueries = append(authQueries, &rest_model.AuthQueryDetail{
ClientID: stringz.OrEmpty(a.SecondaryExtJwtSigner.ClientId),
HTTPURL: stringz.OrEmpty(a.SecondaryExtJwtSigner.ExternalAuthUrl),
Scopes: a.SecondaryExtJwtSigner.Scopes,
Provider: &provider,
ID: a.SecondaryExtJwtSigner.Id,
TypeID: rest_model.AuthQueryTypeEXTDashJWT,
})
}
return authQueries
}
// RefreshTokenRequest is a wrapper around RefreshClaims to avoid collisions between go-jwt interface requirements and
// zitadel oidc interface names. Implements zitadel op.RefreshTokenRequest
type RefreshTokenRequest struct {
common.RefreshClaims
}
// GetAMR implements op.RefreshTokenRequest
func (r *RefreshTokenRequest) GetAMR() []string {
return r.AuthenticationMethodsReferences
}
// GetAudience implements op.RefreshTokenRequest
func (r *RefreshTokenRequest) GetAudience() []string {
return r.Audience
}
// GetAuthTime implements op.RefreshTokenRequest
func (r *RefreshTokenRequest) GetAuthTime() time.Time {
return r.AuthTime.AsTime()
}
// GetClientID implements op.RefreshTokenRequest
func (r *RefreshTokenRequest) GetClientID() string {
return r.ClientID
}
// GetScopes implements op.RefreshTokenRequest
func (r *RefreshTokenRequest) GetScopes() []string {
return r.Scopes
}
// GetSubject implements op.RefreshTokenRequest
func (r *RefreshTokenRequest) GetSubject() string {
return r.Subject
}
// SetCurrentScopes implements op.RefreshTokenRequest
func (r *RefreshTokenRequest) SetCurrentScopes(scopes []string) {
r.Scopes = scopes
}
func (r *RefreshTokenRequest) GetCertFingerprints() []string {
return r.CertFingerprints
}