websrv

package
v0.0.0-...-0116310 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 19, 2025 License: BSD-3-Clause Imports: 22 Imported by: 0

Documentation

Overview

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Req: go 1.16 or later (embed.FS is N/A on Go 1.15 or lower)

Index

Constants

View Source
const (
	API_FLOOD_CACHE_CLEANUP_INTERVAL uint32 = 5 // 5 seconds

	REGEX_SAFE_API_FLOOD_AREA string = `^[a-z0-9\.\:]+$`
)
View Source
const (
	JwtDefaultExpirationMinutes int64 = 60 * 24       //   1440 m =   1 d
	JwtMaxExpirationMinutes     int64 = 60 * 24 * 360 // 518400 m = 360 d
	JwtMinExpirationMinutes     int64 = 1             //      1 m

	JwtMinLength uint16 = 128  // {{{SYNC-AUTH-JWT-MIN-ALLOWED-LEN}}}
	JwtMaxLength uint16 = 1280 // {{{SYNC-AUTH-JWT-MAX-ALLOWED-LEN}}}

	JwtRegexSerial string = `^[A-Z0-9]{10}\-[A-Z0-9]{10}$`
)
View Source
const (
	VERSION   string = "r.20250118.2358"
	SIGNATURE string = smart.COPYRIGHT

	SERVE_HTTP2 bool = false // HTTP2 still have many bugs and many security flaws, disable

	SERVER_ADDR string = "127.0.0.1" // default
	SERVER_PORT uint16 = 17788       // default

	WEB_PUBLIC_RELATIVE_ROOT_PATH string = "./web-public/"
	DEFAULT_DIRECTORY_INDEX_HTML  string = "index.html"

	DAV_PUBLIC_SAFETY_FILE         string = "./webdav-allow-public-no-auth"
	DAV_STORAGE_RELATIVE_ROOT_PATH string = "./webdav" // do not add trailing slash
	DAV_URL_PATH                   string = "webdav"

	CACHED_EXP_TIME_SECONDS uint32 = assets.CACHED_EXP_TIME_SECONDS * 3 // 24h

	CERTIFICATES_DEFAULT_PATH string = "./ssl/"
	CERTIFICATE_PEM_CRT       string = "cert.crt"
	CERTIFICATE_PEM_KEY       string = "cert.key"
)
View Source
const (
	DEBUG_AUTH bool = false // DO NOT SET this to TRUE in production environments ! it is meant just for development purposes
)
View Source
const (
	REGEX_SAFE_WEB_ROUTE string = `^[_a-zA-Z0-9\-\.@,;\!\/]+$` // SAFETY: SUPPORT ONLY THESE CHARACTERS IN WEB ROUTES ... ; w3s alike ; must support everything in REGEX_SMART_SAFE_FILE_NAME except: #, includding A-Z which on register route may be disallowed
)
View Source
const (
	REGEX_SESS_UUID_COOKIE_VALID_VALUE string = `^[A-Za-z0-9]+` // B62
)
View Source
const TheStrName string = "SmartGO Web Server"
View Source
const TheStrSignature string = TheStrName + " " + VERSION

Variables

View Source
var (
	DEBUG bool = smart.DEBUG
)
View Source
var (
	DEBUG_API_CACHE bool = smart.DEBUG
)

Functions

func ApiFloodControlClearVisitFactorsPerClientIP

func ApiFloodControlClearVisitFactorsPerClientIP(r *http.Request, area string) int64

func ApiFloodControlCountVisitFactorsPerClientIP

func ApiFloodControlCountVisitFactorsPerClientIP(r *http.Request, area string) int64

func ApiFloodControlRegisterClientIPVisit

func ApiFloodControlRegisterClientIPVisit(r *http.Request, area string, factor uint8) int64

func ApiResponseJsonERR

func ApiResponseJsonERR(errCode uint16, errMsg string, data any) string

func ApiResponseJsonOK

func ApiResponseJsonOK(data any) string

func AuthTokenJwtAlgoGet

func AuthTokenJwtAlgoGet() string

func AuthTokenJwtAlgoSet

func AuthTokenJwtAlgoSet(jwtAlgo string) bool

func AuthTokenJwtAlgoValidGet

func AuthTokenJwtAlgoValidGet(algo string) string

func AuthTokenJwtIsEnabled

func AuthTokenJwtIsEnabled() bool

func DecryptUrlValue

func DecryptUrlValue(val string) string

func EncryptUrlValue

func EncryptUrlValue(val string) string

func Get2FATotp

func Get2FATotp(secret string) (string, *otp.TOTP, error)

func GetAllPostVars

func GetAllPostVars(r *http.Request) map[string][]string

func GetAllRequestVars

func GetAllRequestVars(r *http.Request) map[string][]string

func GetAllUrlQueryVars

func GetAllUrlQueryVars(r *http.Request) map[string][]string

func GetAuthRealm

func GetAuthRealm() string

func GetBaseBrowserPath

func GetBaseBrowserPath() string

func GetBaseDomainDomainPort

func GetBaseDomainDomainPort(r *http.Request) (string, string, string, error)

func GetBasePath

func GetBasePath() string

func GetClientIdentAppSafeSignature

func GetClientIdentAppSafeSignature(r *http.Request) string

func GetClientIdentUidHash

func GetClientIdentUidHash(r *http.Request) string

func GetClientMimeAcceptHeaders

func GetClientMimeAcceptHeaders(r *http.Request) []string

func GetCookie

func GetCookie(r *http.Request, name string) string

func GetCurrentBaseUrl

func GetCurrentBaseUrl(r *http.Request) string

func GetCurrentBrowserBaseUrl

func GetCurrentBrowserBaseUrl(r *http.Request) string

func GetCurrentBrowserPath

func GetCurrentBrowserPath(r *http.Request) string

func GetCurrentBrowserProtocol

func GetCurrentBrowserProtocol(r *http.Request) string

func GetCurrentBrowserUrl

func GetCurrentBrowserUrl(r *http.Request, withUrlQuery bool) string

func GetCurrentPath

func GetCurrentPath(r *http.Request) string

func GetCurrentProtocol

func GetCurrentProtocol(r *http.Request) string

func GetCurrentUrl

func GetCurrentUrl(r *http.Request, withUrlQuery bool) string

func GetCurrentYear

func GetCurrentYear() string

func GetPostVar

func GetPostVar(r *http.Request, key string) string

func GetPostVars

func GetPostVars(r *http.Request, key string) []string

func GetRequestVar

func GetRequestVar(r *http.Request, key string) string

func GetRequestVars

func GetRequestVars(r *http.Request, key string) []string

func GetUrlQueryVar

func GetUrlQueryVar(r *http.Request, key string) string

func GetUrlQueryVars

func GetUrlQueryVars(r *http.Request, key string) []string

func GetUrlRawQuery

func GetUrlRawQuery(r *http.Request) string

func GetUuidCookieName

func GetUuidCookieName() string

func GetUuidCookieValue

func GetUuidCookieValue(r *http.Request) string

func GetVisitorRealIpAddr

func GetVisitorRealIpAddr(r *http.Request) (bool, string)

func GetVisitorRemoteIpAddrAndPort

func GetVisitorRemoteIpAddrAndPort(r *http.Request) (string, string)

func HttpRequestGetHeaderStr

func HttpRequestGetHeaderStr(r *http.Request, hdrKey string) string

func Is2FATotpSecretValid

func Is2FATotpSecretValid(secret string) bool

func IsAjaxRequest

func IsAjaxRequest(r *http.Request) bool

func IsSessUUIDCookieValid

func IsSessUUIDCookieValid(crrUUIDCookieVal string) bool

func JsonValidateWithSchema

func JsonValidateWithSchema(draft uint16, schema string, json string) error

func JwtAudienceIsDefaultArea

func JwtAudienceIsDefaultArea(jwtAudience JwtAudience) bool

func JwtAudienceIsDefaultPrivs

func JwtAudienceIsDefaultPrivs(jwtAudience JwtAudience) bool

func JwtAudienceIsDefaultRestr

func JwtAudienceIsDefaultRestr(jwtAudience JwtAudience) bool

func JwtGetFullNameSigningAlgo

func JwtGetFullNameSigningAlgo(jwtSignMethod string) string

func JwtNewAudience

func JwtNewAudience(ipList string, area string, privs string, restr string, cliSign string) []string

func JwtVerifyWithPublicKey

func JwtVerifyWithPublicKey(tokenString string, jwtSignMethod string, clientIP string, dom string, port string, userName string, publicKey string) error

func JwtVerifyWithUserPrivKey

func JwtVerifyWithUserPrivKey(tokenString string, jwtSignMethod string, clientIP string, dom string, port string, userName string, userPrivKey string) error

func ParseForm

func ParseForm(r *http.Request) error

func ParseMultipartForm

func ParseMultipartForm(r *http.Request) error

func ParseUrlRawQuery

func ParseUrlRawQuery(urlQuery string) map[string][]string

func RequestHaveQueryString

func RequestHaveQueryString(r *http.Request) bool

func SetWebAuthAccountsMethods

func SetWebAuthAccountsMethods(methods WebAuthAccountsMethods) error

func UrlHandlerRegisterRoute

func UrlHandlerRegisterRoute(route string, skipAuth bool, methods []string, maxTailSegments int, fxHandler HttpHandlerFunc) bool

func UrlHandlerUnRegisterRoute

func UrlHandlerUnRegisterRoute(route string) bool

func WebDirExists

func WebDirExists(path string) bool

func WebDirIsValid

func WebDirIsValid(path string) bool

func WebFileExists

func WebFileExists(path string) bool

func WebPathExists

func WebPathExists(path string) bool

func WebPathIsValid

func WebPathIsValid(path string) bool

func WebServerRun

func WebServerRun(servePublicPath bool, webdavOptions *WebdavRunOptions, serveSecure bool, certifPath string, httpAddr string, httpPort uint16, timeoutSeconds uint32, allowedIPs string, authRealm string, authUser string, authPass string, authToken string, customAuthCheck smarthttputils.HttpAuthCheckFunc, rateLimit int, rateBurst int) int16

IMPORTANT: If using Proxy with different PROXY_HTTP_BASE_PATH than "/" (ex: "/api/") the Proxy MUST strip back PROXY_HTTP_BASE_PATH to "/" for this backend

func WebServerSetMaxPostSize

func WebServerSetMaxPostSize(size uint64) bool

func WebUrlPathIsValid

func WebUrlPathIsValid(urlPath string) bool

func WebUrlRouteIsValid

func WebUrlRouteIsValid(route string) bool

Types

type HttpHandlerFunc

type HttpHandlerFunc func(r *http.Request, headPath string, tailPaths []string, authData smart.AuthDataStruct) (response HttpResponse)
var RouteHandlerAuthApi HttpHandlerFunc = func(r *http.Request, headPath string, tailPaths []string, authData smart.AuthDataStruct) (response HttpResponse) {

	defer smart.PanicHandler()

	if len(tailPaths) > 1 {
		response.StatusCode = 400
		response.ContentBody = "Auth Api: Requested Route is Too Long"
		response.ContentFileName = "400.html"
		return
	}
	//--
	var subRoute string = ""
	if len(tailPaths) > 0 {
		subRoute = tailPaths[0]
	}

	response.StatusCode = 208
	response.ContentFileName = "error.json"
	//--
	var jwtSignMethod string = AuthTokenJwtAlgoGet()

	switch subRoute {
	case "":
		if (!IsAjaxRequest(r)) && (RequestHaveQueryString(r)) {
			response.StatusCode = 302
			response.ContentBody = GetCurrentBrowserPath(r)
			response.ContentFileName = ""
			return
		}
		if r.Method != "GET" {
			response.ContentBody = ApiResponseJsonERR(405, "Unsupported Request Method: `"+r.Method+"`", nil)
			return
		}
		if authData.OK != true {
			response.ContentBody = ApiResponseJsonERR(403, "Authentication is Required for this URL", nil)
			return
		}
		if authData.ErrMsg != "" {
			response.LogMessage = "Authentication Error: `" + authData.ErrMsg + "`"
			response.ContentBody = ApiResponseJsonERR(500, "Authentication Error", nil)
			return
		}
		if (smart.StrTrimWhitespaces(authData.UserName) == "") || (smart.AuthIsValidExtUserName(authData.UserName) != true) {
			response.LogMessage = "Authentication Ext. UserName is Empty or Invalid: `" + authData.UserName + "`"
			response.ContentBody = ApiResponseJsonERR(422, "Authentication Ext. UserName is Empty or Invalid", nil)
			return
		}
		userMetaInfoDateTime, errUserMetaInfoDateTime := smart.AuthGetMetaData(authData, "DateTime")
		if errUserMetaInfoDateTime != nil {
			if len(authData.MetaData) > 0 {
				response.ContentBody = ApiResponseJsonERR(500, "Failed to Get User`s MetaInfo DateTime", errUserMetaInfoDateTime)
				return
			}
		}
		userMetaInfoDateTime = smart.StrTrimWhitespaces(userMetaInfoDateTime)
		if userMetaInfoDateTime == "" {
			if len(authData.MetaData) > 0 {
				response.ContentBody = ApiResponseJsonERR(500, "Failed to Get User`s MetaInfo DateTime: EMPTY", nil)
				return
			}
		}
		var tkTyp string = ""
		if AuthTokenJwtIsEnabled() == true {
			tkTyp = jwtSignMethod
			if tkTyp != "" {
				tkTyp = "JWT:" + tkTyp
			}
		}
		var tkOpqTyp string = ""
		if smart.AuthTokenIsEnabled() == true {
			tkOpqTyp = smart.OPAQUE_TOKEN_FULL_NAME
		}
		var thePassHash string = authData.PassHash
		if authData.PassAlgo == smart.ALGO_PASS_PLAIN {
			thePassHash = "[Encrypted]:" + thePassHash
		} else if authData.PassAlgo == smart.ALGO_PASS_NONE {
			if thePassHash != "" {
				thePassHash = "ERROR:(Not Empty)"
			}
		}
		var safeMetaData map[string]string = nil // safe for display, will hide (mask) sensitive data
		if authData.MetaData != nil {
			safeMetaData = map[string]string{}
			if len(authData.MetaData) > 0 {
				for kk, vv := range authData.MetaData {
					var canDisplayData bool = true
					if smart.StrIContains(kk, "key") {
						canDisplayData = false
					} else if smart.StrIContains(kk, "security") {
						canDisplayData = false
					} else if smart.StrIContains(kk, "private") {
						canDisplayData = false
					} else if smart.StrIContains(kk, "pass") {
						canDisplayData = false
					}
					if !canDisplayData {
						safeMetaData[kk+":length"] = smart.ConvertIntToStr(len(vv))
					} else {
						safeMetaData[kk] = vv
					}
				}
			}
		}
		var jwtInfo *JwtTokenData = nil
		if authData.PassAlgo == smart.ALGO_PASS_SMART_SAFE_WEB_TOKEN {
			jwtExInfo := JwtExtractData(authData.TokenData)
			jwtInfo = &jwtExInfo
		}
		metaInfo := authMetaNfo{
			Auth2FAEnabled:          smart.Auth2FACookieIsEnabled(),
			AuthBasicEnabled:        smart.AuthBasicIsEnabled(),
			AuthTokenEnabled:        smart.AuthTokenIsEnabled(),
			AuthCookieEnabled:       smart.AuthCookieIsEnabled(),
			AuthBearerEnabled:       smart.AuthBearerIsEnabled(),
			AuthApiKeyEnabled:       smart.AuthApikeyIsEnabled(),
			AuthSignedTokensDefAlgo: tkTyp,
			AuthOpaqueTokensDefAlgo: tkOpqTyp,
		}
		status := authStatusNfo{
			Authenticated:       authData.OK,
			AuthErrors:          authData.ErrMsg,
			AuthMethodID:        authData.Method,
			AuthMethodName:      "Auth:" + smart.AuthMethodGetNameById(authData.Method),
			AuthRealm:           authData.Realm,
			AuthArea:            authData.Area,
			AuthUserName:        authData.UserName,
			AuthUserID:          authData.UserID,
			AuthPassHash:        thePassHash,
			AuthPassAlgoID:      authData.PassAlgo,
			AuthPassAlgoName:    smart.AuthPassHashAlgoGetNameById(authData.PassAlgo),
			AuthRawDataSize:     uint64(len(authData.RawAuthData)),
			AuthTokenSize:       uint64(len(authData.TokenData)),
			AuthTokenType:       authData.TokenAlgo,
			AuthEmailAddr:       authData.EmailAddr,
			AuthFullName:        authData.FullName,
			AuthPrivileges:      authData.Privileges,
			AuthRestrictions:    authData.Restrictions,
			AuthSecurityKeySize: uint64(len(authData.SecurityKey)),
			AuthPrivateKeySize:  uint64(len(authData.PrivKey)),
			AuthPublicKeySize:   uint64(len(authData.PubKey)),
			AuthPublicKey:       authData.PubKey,
			AuthQuota:           authData.Quota,
			AuthMetaData:        safeMetaData,
			AuthJwtToken:        jwtInfo,
		}
		nfo := authNfo{
			Status:   &status,
			MetaInfo: &metaInfo,
		}
		if DEBUG_AUTH {
			nfo.DebugData = &authData
		}
		arrAccepts := GetClientMimeAcceptHeaders(r)
		acceptJson := smart.InListArr(smarthttputils.MIME_TYPE_JSON, arrAccepts)
		if acceptJson {
			response.ContentFileName = "auth.json"
			response.ContentBody = ApiResponseJsonOK(nfo)
		} else {
			var bwPath string = GetCurrentBrowserPath(r)
			var title = "Auth Info"
			var headHtml string = assets.HTML_CSS_STYLE_PREFER_COLOR_DARK + "\n"
			var bodyHtml string = "<h1>" + smart.EscapeHtml(title) + "</h1>" + "\n"
			bodyHtml += `<div class="operation_hint">API access point &nbsp;<i class="sfi sfi-lock sfi-xl" title="Requires Authentication" style="cursor:help;"></i> [Accept: ` + smart.EscapeHtml(smarthttputils.MIME_TYPE_JSON) + `]: <i>` + smart.EscapeHtml("`"+bwPath+"`") + `</i></div>` + "\n"
			bodyHtml += smart.RenderMarkersTpl(assets.ReadWebAsset("lib/tpl/syntax-highlight-init.inc.mtpl.htm"), map[string]string{
				"THEME": "",
				"AREAS": "body",
			})
			bodyHtml += smart.RenderMarkersTpl(assets.ReadWebAsset("lib/tpl/syntax-highlight-area.inc.mtpl.htm"), map[string]string{
				"SYNTAX": "json",
				"CODE":   smart.JsonNoErrChkEncode(nfo, true, false),
			})

			response.ContentBody = srvassets.HtmlServerFaviconTemplate(title, headHtml, bodyHtml, true, assets.GetAuthLogo(false))
			response.ContentFileName = "auth.html"
		}
		response.StatusCode = 200
		return
		break
	case "2fatotp":
		if RequestHaveQueryString(r) {
			response.StatusCode = 302
			response.ContentBody = GetCurrentBrowserPath(r)
			response.ContentFileName = ""
			return
		}
		if r.Method != "GET" {
			response.StatusCode = 405
			response.LogMessage = "Unsupported Request Method: `" + r.Method + "`"
			response.ContentBody = response.LogMessage
			response.ContentFileName = "405.html"
			return
		}
		if authData.OK != true {
			response.StatusCode = 403
			response.LogMessage = "Authentication is Required for this URL"
			response.ContentBody = response.LogMessage
			response.ContentFileName = "403.html"
			return
		}
		if authData.ErrMsg != "" {
			response.StatusCode = 500
			response.LogMessage = "Authentication Error: `" + authData.ErrMsg + "`"
			response.ContentBody = "Authentication Error"
			response.ContentFileName = "500.html"
			return
		}

		if (authData.Method != smart.HTTP_AUTH_MODE_BASIC) && (authData.Method != smart.HTTP_AUTH_MODE_COOKIE) {
			response.StatusCode = 406
			response.ContentBody = "Authentication Method is Not Accepted for this URL: [" + smart.ConvertUInt8ToStr(authData.Method) + "] / [Auth:" + smart.AuthMethodGetNameById(authData.Method) + "]"
			response.ContentFileName = "406.html"
			return
		}
		if authData.Area != smart.HTTP_AUTH_DEFAULT_AREA {
			response.StatusCode = 424
			response.ContentBody = "Authentication Area is Not Accepted for this URL"
			response.ContentFileName = "424.html"
			return
		}
		if smart.AuthSafeTestPrivsRestr(authData.Privileges, smart.HTTP_AUTH_ADMIN_PRIV) != true {
			response.StatusCode = 403
			response.ContentBody = "Authentication Privileges are Not Accepted for this URL"
			response.ContentFileName = "403.html"
			return
		}

		if (smart.StrTrimWhitespaces(authData.UserName) == "") || (smart.AuthIsValidUserName(authData.UserName) != true) {
			response.StatusCode = 422
			response.ContentBody = "Authentication UserName is Empty or Invalid: `" + authData.UserName + "`"
			response.ContentFileName = "422.html"
			return
		}
		basedom, dom, port, errDomPort := GetBaseDomainDomainPort(r)
		if errDomPort != nil {
			response.StatusCode = 502
			response.ContentBody = "Authentication Domain:Port Failed: `" + errDomPort.Error() + "`"
			response.ContentFileName = "502.html"
			return
		}
		if (basedom == "") || (dom == "") || (port == "") {
			response.StatusCode = 502
			response.ContentBody = "Authentication Domain:Port is Invalid: `" + dom + ":" + port + "` ; base domain is: `" + basedom + "`"
			response.ContentFileName = "502.html"
			return
		}
		rndSecret, totp, errTotp := Get2FATotp("")
		if (totp == nil) || (errTotp != nil) {
			response.StatusCode = 500
			response.ContentBody = "TOTP Error"
			if errTotp != nil {
				response.ContentBody += ": `" + errTotp.Error() + "`"
			}
			response.ContentFileName = "500.html"
			return
		}
		var qrUrl string = totp.GenerateBarcodeUrl(authData.UserName, basedom)

		svgQR, errSvgQR := qrsvg.New(qrUrl, "M", "#685A8B", "none", true, 4, 2)
		if errSvgQR != nil {
			response.StatusCode = 500
			response.ContentBody = "QR Code Error: `" + errSvgQR.Error() + "`"
			response.ContentFileName = "500.html"
			return
		}
		var title = "Auth 2FA TOTP Generator"
		//	var headHtml string = "<style>img.svg { margin:10px; border:1px #EFEFEF solid; }</style>" + "\n"
		var headHtml string = assets.HTML_CSS_STYLE_PREFER_COLOR_DARK + "\n" + "<style>img.svg { margin:10px; }</style>" + "\n"
		var bodyHtml string = "<h1>" + smart.EscapeHtml(title) + "</h1>" + "\n"
		bodyHtml += `<hr>` + "\n"
		bodyHtml += `<h5>2FA Setup QRCode to use with <i style="color:#ED2839;">FreeOTP App</i> or similar 2FA authenticator apps:</h5><img class="svg" src="` + smart.EscapeHtml(smart.DATA_URL_SVG_IMAGE_PREFIX+smart.EscapeUrl(svgQR.Svg)) + `" title="` + smart.EscapeHtml(qrUrl) + `">` + "\n"
		bodyHtml += `<br>` + "\n"
		bodyHtml += `<button class="ux-button ux-button-primary" onclick="self.location = self.location; return false;"><i class="sfi sfi-lg sfi-spinner9"></i>&nbsp; Generate New 2FA TOTP Secret</button>`
		bodyHtml += `<hr>` + "\n"
		bodyHtml += `<textarea id="area-secret" class="ux-field" style="width:320px; height:25px; font-size:0.625rem !important; color:#CDCDCD !important;" readonly>` + smart.EscapeHtml(rndSecret) + `</textarea>` + "\n"
		bodyHtml += `<br>` + "\n"
		bodyHtml += `<script>const copyElemToClipboard = () => { const err = smartJ$Browser.copyToClipboard('area-secret'); const txt = 'Copy to Clipboard'; const img = '<br><i class="sfi sfi-clipboard"></i>'; if(!!err) { console.error('ERR: copyElemToClipboard:', err); smartJ$Browser.GrowlNotificationAdd(txt, 'FAILED to Copy the Secret to Clipboard' + img, null, 3500, false, 'pink'); } else { smartJ$Browser.GrowlNotificationAdd(txt, 'Secret has been Copied to Clipboard' + img, null, 1500, false, 'blue'); } };</script>` + "\n"
		bodyHtml += `<button class="ux-button ux-button-small ux-button-details" onclick="copyElemToClipboard(); return false;"><i class="sfi sfi-stack"></i>&nbsp; Copy Secret to Clipboard</button>` + `&nbsp; <span style="color:#685A8B;">[&nbsp;username:&nbsp;` + "`<b>" + smart.EscapeHtml(authData.UserName) + "</b>`" + `&nbsp;]</span>` + "\n"
		bodyHtml += `<br>` + "\n"
		response.ContentBody = srvassets.HtmlServerFaviconTemplate(title, headHtml, bodyHtml, true, assets.GetAuthLogo(false))
		response.StatusCode = 200
		response.ContentFileName = "auth-2fatotp.html"
		return
		break
	case "jwt":
		if r.Method != "GET" {
			response.LogMessage = "Unsupported Request Method: `" + r.Method + "`"
			response.ContentBody = ApiResponseJsonERR(405, response.LogMessage, nil)
			return
		}
		if authData.OK != true {
			response.LogMessage = "Authentication is Required for this URL"
			response.ContentBody = ApiResponseJsonERR(403, response.LogMessage, nil)
			return
		}
		if authData.ErrMsg != "" {
			response.LogMessage = "Authentication Error: `" + authData.ErrMsg + "`"
			response.ContentBody = ApiResponseJsonERR(500, "Authentication Error", nil)
			return
		}

		if (authData.Method != smart.HTTP_AUTH_MODE_BASIC) && (authData.Method != smart.HTTP_AUTH_MODE_COOKIE) && (authData.Method != smart.HTTP_AUTH_MODE_BEARER) {
			response.ContentBody = ApiResponseJsonERR(406, "Authentication Method is Not Accepted for this URL: ["+smart.ConvertUInt8ToStr(authData.Method)+"] / [Auth:"+smart.AuthMethodGetNameById(authData.Method)+"]", nil)
			return
		}
		if authData.Area != smart.HTTP_AUTH_DEFAULT_AREA {
			response.ContentBody = ApiResponseJsonERR(424, "Authentication Area is Not Accepted for this URL", nil)
			return
		}
		if smart.AuthSafeTestPrivsRestr(authData.Privileges, smart.HTTP_AUTH_ADMIN_PRIV) != true {
			response.ContentBody = ApiResponseJsonERR(403, "Authentication Privileges are Not Accepted for this URL", nil)
			return
		}
		if AuthTokenJwtIsEnabled() != true {
			response.ContentBody = ApiResponseJsonERR(501, "Authentication JWT is Disabled", nil)
			return
		}
		if smart.StrTrimWhitespaces(jwtSignMethod) == "" {
			response.ContentBody = ApiResponseJsonERR(501, "Authentication JWT Algo is Empty or N/A", nil)
			return
		}
		if smart.AuthBearerIsEnabled() != true {
			response.ContentBody = ApiResponseJsonERR(423, "This URL is available just when the Auth Bearer is Enabled", nil)
			return
		}
		if (smart.StrTrimWhitespaces(authData.UserName) == "") || (smart.AuthIsValidUserName(authData.UserName) != true) {
			response.ContentBody = ApiResponseJsonERR(422, "Authentication UserName is Empty or Invalid: `"+authData.UserName+"`", nil)
			return
		}

		if (authData.SecurityKey == "") || (!smart.AuthIsValidSecurityKey(authData.SecurityKey)) {
			response.ContentBody = ApiResponseJsonERR(409, "Authenticated User`s SecurityKey is Unavailable or Invalid", nil)
			return
		}
		//--
		var expMinutesStr string = smart.StrTrimWhitespaces(GetUrlQueryVar(r, "expMinutes"))
		if expMinutesStr == "" {
			expMinutesStr = smart.ConvertInt64ToStr(JwtDefaultExpirationMinutes)
		}
		var expMinutes int64 = 0
		if (len(expMinutesStr) >= 1) && (len(expMinutesStr) <= 6) {
			expMinutes = smart.ParseStrAsInt64(expMinutesStr)
		}
		if expMinutes < JwtMinExpirationMinutes {
			expMinutes = JwtMinExpirationMinutes
		} else if expMinutes > JwtMaxExpirationMinutes {
			expMinutes = JwtMaxExpirationMinutes
		}
		//--
		var ipAddress string = smart.StrTrimWhitespaces(GetUrlQueryVar(r, "ipAddress"))
		if len(ipAddress) > 255 {
			ipAddress = ""
		}
		if ipAddress == "" {
			ipAddress = "*"
		}
		var allowedIpList string = "*"
		var chkIp string = "*"
		if ipAddress != "*" {
			arrIps := smart.Explode(",", ipAddress)
			var safeIps []string = []string{}
			for i := 0; i < len(arrIps); i++ {
				arrIps[i] = smart.StrTrimWhitespaces(arrIps[i])
				if (arrIps[i] != "") && (arrIps[i] != "*") {
					if smart.IsNetValidIpAddr(arrIps[i]) {
						if !smart.InListArr(arrIps[i], safeIps) {
							safeIps = append(safeIps, arrIps[i])
						}
					}
				}
				if i >= 3 {
					break
				}
			}
			ipAddress = smart.StrTrimWhitespaces(smart.Implode(", ", safeIps))
			if ipAddress == "" {
				ipAddress = "*"
			}
			for i := 0; i < len(safeIps); i++ {
				chkIp = safeIps[i]
				safeIps[i] = "<" + safeIps[i] + ">"
			}
			allowedIpList = smart.StrTrimWhitespaces(smart.Implode(",", safeIps))
			if allowedIpList == "" {
				allowedIpList = "*"
			}
			if allowedIpList != "*" {
				errValidateAllowedIpList := smart.ValidateIPAddrList(allowedIpList)
				if errValidateAllowedIpList != nil {
					allowedIpList = "*"
				}
			}
		}

		data, errData := ApiAuthNewBearerTokenJwt(r, expMinutes, authData.UserName, authData.SecurityKey, allowedIpList, chkIp, "")
		if errData != nil {
			response.ContentBody = ApiResponseJsonERR(500, "JWT Token ERR: "+errData.Error(), nil)
			return
		}

		arrAccepts := GetClientMimeAcceptHeaders(r)
		acceptJson := smart.InListArr(smarthttputils.MIME_TYPE_JSON, arrAccepts)
		if acceptJson {
			response.ContentBody = ApiResponseJsonOK(data)
			response.ContentFileName = "auth-token.json"
		} else {
			var bwPath string = GetCurrentBrowserPath(r)
			var title = "JWT Access Token Generator"
			var headHtml string = assets.HTML_CSS_STYLE_PREFER_COLOR_DARK + "\n"
			var bodyHtml string = "<h1>" + smart.EscapeHtml(title) + "</h1>" + "\n"
			bodyHtml += `<hr>` + "\n"
			bodyHtml += `<div class="operation_hint">API access point &nbsp;<i class="sfi sfi-lock sfi-xl" title="Requires Authentication" style="cursor:help;"></i> [Accept: ` + smart.EscapeHtml(smarthttputils.MIME_TYPE_JSON) + `]: <i>` + smart.EscapeHtml("`"+bwPath+"`") + `</i> ; Query Parameters: (<i>?expMinutes=` + smart.ConvertInt64ToStr(JwtMinExpirationMinutes) + `..` + smart.ConvertInt64ToStr(JwtMaxExpirationMinutes) + `</i>)</div>` + "\n"
			bodyHtml += smart.RenderMarkersTpl(assets.ReadWebAsset("lib/tpl/syntax-highlight-init.inc.mtpl.htm"), map[string]string{
				"THEME": "",
				"AREAS": "body",
			})
			bodyHtml += smart.RenderMarkersTpl(assets.ReadWebAsset("lib/tpl/syntax-highlight-area.inc.mtpl.htm"), map[string]string{
				"SYNTAX": "json",
				"CODE":   smart.JsonNoErrChkEncode(data, true, false),
			})

			bodyHtml += `<br>` + "\n"
			bodyHtml += `<b>LifeTime&nbsp;(minutes):</b>&nbsp;<input type="number" placeholder="1234" id="mins" maxlength="6" title="Min: ` + smart.EscapeHtml(smart.ConvertInt64ToStr(JwtMinExpirationMinutes)) + ` ; Max: ` + smart.EscapeHtml(smart.ConvertInt64ToStr(JwtMaxExpirationMinutes)) + ` ; Default: ` + smart.EscapeHtml(smart.ConvertInt64ToStr(JwtDefaultExpirationMinutes)) + ` " class="ux-field" value="` + smart.ConvertInt64ToStr(expMinutes) + `" min="` + smart.ConvertInt64ToStr(JwtMinExpirationMinutes) + `" max="` + smart.ConvertInt64ToStr(JwtMaxExpirationMinutes) + `" autocomplete="off" style="width:100px; text-align:center;">` + "\n"
			bodyHtml += `<b>IPAddresses&nbsp;(Ipv4/Ipv6):</b>&nbsp;<input type="text" id="ip" maxlength="255" placeholder="127.0.0.1, ::1" title="IP List separed by comma, or wildcard * for any IP" class="ux-field" value="` + ipAddress + `" autocomplete="off" style="width:200px; text-align:center;">` + "\n"
			bodyHtml += `<button class="ux-button ux-button-regular" onclick="let mins = smartJ$Utils.format_number_int(jQuery('input#mins').val(), false); if((!mins) || (!smartJ$Utils.isFiniteNumber(mins)) || (mins <= 0)) { smartJ$Browser.GrowlNotificationAdd('Error', '&lt;h5&gt;Invalid or Non-Numeric Expression&lt;/h5&gt;', '', 3500, false, 'pink'); } else { let ipAddr = jQuery('input#ip').val(); setTimeout(() => { self.location = '` + smart.EscapeJs(bwPath) + `?expMinutes=' + smartJ$Utils.escape_url(mins) + '&ipAddress=' + smartJ$Utils.escape_url(ipAddr); }, 50); }"><i class="sfi sfi-lg sfi-spinner10"></i>&nbsp; Generate New JWT Access Token</button>` + "\n"
			bodyHtml += `<hr>` + "\n"
			bodyHtml += `<textarea id="area-secret" class="ux-field" style="min-width:700px; width:100%; height:50px; font-size:0.625rem !important; color:#CDCDCD !important;" readonly>` + smart.EscapeHtml(data.Token) + `</textarea>` + "\n"
			bodyHtml += `<br>` + "\n"
			bodyHtml += `<script>const copyElemToClipboard = () => { const err = smartJ$Browser.copyToClipboard('area-secret'); const txt = 'Copy to Clipboard'; const img = '<br><i class="sfi sfi-clipboard"></i>'; if(!!err) { console.error('ERR: copyElemToClipboard:', err); smartJ$Browser.GrowlNotificationAdd(txt, 'FAILED to Copy the Secret to Clipboard' + img, null, 3500, false, 'pink'); } else { smartJ$Browser.GrowlNotificationAdd(txt, 'Secret has been Copied to Clipboard' + img, null, 1500, false, 'blue'); } };</script>` + "\n"
			bodyHtml += `<button class="ux-button ux-button-small ux-button-details" onclick="copyElemToClipboard(); return false;"><i class="sfi sfi-stack"></i>&nbsp; Copy Secret to Clipboard</button>` + `&nbsp; <span style="color:#685A8B;">[&nbsp;username:&nbsp;` + "`<b>" + smart.EscapeHtml(authData.UserName) + "</b>`" + `&nbsp;]</span>` + "\n"
			bodyHtml += `<br>` + "\n"
			response.ContentBody = srvassets.HtmlServerFaviconTemplate(title, headHtml, bodyHtml, true, assets.GetAuthLogo(false))
			response.ContentFileName = "auth.html"
		}
		response.StatusCode = 200
		return
		break
	default:

	}

	response.StatusCode = 404
	response.LogMessage = "Invalid Auth Api Request Sub-Path"
	response.ContentBody = response.LogMessage
	response.ContentFileName = "404.html"
	return


} //end fx

-- auth token api

var RouteHandlerFaviconStream HttpHandlerFunc = func(r *http.Request, headPath string, tailPaths []string, authData smart.AuthDataStruct) (response HttpResponse) {

	defer smart.PanicHandler()

	response.Headers = map[string]string{}
	response.Headers["Z-Content"] = "Streaming"
	//--
	var fName string = "favicon.svg"
	var fPath string = WEB_PUBLIC_RELATIVE_ROOT_PATH + fName
	if !smart.PathIsFile(fPath) {
		response.StatusCode = 500
		response.ContentBody = "Streaming File cannot be found: `" + fName + "`"
		response.ContentFileName = "500.html"
		return
	}
	response.ContentFileName = fName
	response.ContentStream = func() (ioReadStream io.Reader) {
		ioReadStream, fErr := os.Open(fPath)
		if fErr != nil {
			log.Println("[ERROR]", "Streaming Handler File `"+fPath+"` Open Error", fErr)
			return
		}
		return
	}

	return


} //end fx

-- favicon (streaming) page (svg)

var RouteHandlerHomePage HttpHandlerFunc = func(r *http.Request, headPath string, tailPaths []string, authData smart.AuthDataStruct) (response HttpResponse) {

	defer smart.PanicHandler()

	response.StatusCode = 200
	const title string = "WebApp"
	var headHtml string = assets.HTML_CSS_STYLE_PREFER_COLOR_DARK + "\n" + "<style>" + "\n" + "div.app { text-align:center; margin:20px; } div.app * { color: #ED2839 !important; }" + "\n" + "</style>"
	var bodyHtml string = `<center><div class="app" style="background:#FFFFFF; width:552px; border-radius:7px;">` + "<h1>" + smart.EscapeHtml(TheStrName) + "</h1>" + "\n" + `<img alt="app:svg" title="` + smart.EscapeHtml(title) + `" width="512" height="512" src="` + smart.EscapeHtml(assets.GetAppLogo(false)) + `"></div></center>` + "\n"
	response.ContentBody = assets.HtmlStandaloneFaviconTemplate(title, headHtml, bodyHtml, false, assets.GetAppLogo(true))
	response.ContentFileName = "webapp.html"

	response.ContentDisposition = ""
	response.CacheExpiration = -1
	response.CacheLastModified = ""
	response.CacheControl = smarthttputils.CACHE_CONTROL_NOCACHE
	response.Headers = nil
	response.Cookies = nil
	response.LogMessage = ""

	return


} //end fx

-- home page (html)

var RouteHandlerInfoPage HttpHandlerFunc = func(r *http.Request, headPath string, tailPaths []string, authData smart.AuthDataStruct) (response HttpResponse) {

	defer smart.PanicHandler()

	remoteAddr, remotePort := GetVisitorRemoteIpAddrAndPort(r)
	_, realClientIp := GetVisitorRealIpAddr(r)
	basedom, dom, port, _ := GetBaseDomainDomainPort(r)
	var proxySetDetected string = smart.GetHttpProxyRealServerHostPortHeaderKey() // Proxy IP:port (if used) ; Go Server Port (may differ from the above external port)
	var proxyRealClientIp string = smart.GetHttpProxyRealClientIpHeaderKey()
	var proxyRealIpStatus string = "NoProxy"
	if proxyRealClientIp != "" {
		proxyRealIpStatus = "Proxy:" + proxyRealClientIp
	}
	signature := smart.GetHttpUserAgentFromRequest(r)
	bw, cls, os, mb := smart.GetUserAgentBrowserClassOs(signature)
	var isMobile string = "no"
	if mb == true {
		isMobile = "yes"
	}
	response.StatusCode = 208
	const title string = "Service Info"
	var headHtml string = assets.HTML_META_ROBOTS_NOINDEX + "\n" + assets.HTML_CSS_STYLE_PREFER_COLOR_DARK
	var bodyHtml string = `<h1 style="display:inline-block;">`

	bodyHtml += smart.EscapeHtml(title)
	bodyHtml += `</h1>`
	bodyHtml += `<h4>` + smart.Nl2Br(smart.EscapeHtml(TheStrSignature)) + `</h4>`
	bodyHtml += `<hr>`
	bodyHtml += "Client Real-IP [" + smart.EscapeHtml(proxyRealIpStatus) + "] is: <b>`" + smart.EscapeHtml(realClientIp) + "`</b> ; Remote-IP (Host:Port) is: " + smart.EscapeHtml("`"+remoteAddr+"`:`"+remotePort+"`") + "<br>"
	bodyHtml += "Client UserAgent: <i>`" + smart.EscapeHtml(signature) + "`</i>" + "<br>"
	bodyHtml += `<div style="margin-top:4px; margin-bottom:12px;" title="` + smart.EscapeHtml("Client Browser Class: "+"`"+cls+"`"+" ; Client is Mobile: "+"`"+isMobile+"`") + `">`
	bodyHtml += "\n"
	bodyHtml += `<img src="` + smart.EscapeHtml(assets.GetClientBwLogo(bw, true)) + `" height="64" style="margin-right:12px; cursor:help;" alt="image-cli-bw" title="Client Browser: ` + smart.EscapeHtml("`"+bw+"`") + `">`
	bodyHtml += "\n"
	bodyHtml += `<img src="` + smart.EscapeHtml(assets.GetClientOSLogo(os, true)) + `" height="64" style="margin-right:12px; cursor:help;" alt="image-cli-os" title="Client OS: ` + smart.EscapeHtml("`"+os+"`") + `">`
	bodyHtml += "\n"
	bodyHtml += `</div>`
	bodyHtml += `<hr>`
	bodyHtml += "Server Proxy: <b>`" + smart.EscapeHtml(proxySetDetected) + "`</b>" + "<br>"
	bodyHtml += "Server Protocol: <b>`" + smart.EscapeHtml(smart.GetHttpProtocolFromRequest(r)) + "`</b>" + "<br>"
	bodyHtml += "Server BaseDomain: `" + smart.EscapeHtml(basedom) + "`" + "<br>"
	bodyHtml += "Server Domain: <b>`" + smart.EscapeHtml(dom) + "`</b>" + "<br>"
	bodyHtml += "Server Port: `" + smart.EscapeHtml(port) + "`" + "<br>"
	bodyHtml += "Server Base Path: <b>`" + smart.EscapeHtml(GetBaseBrowserPath()) + "`</b>" + " ; Internal Route Base Path: `" + smart.EscapeHtml(GetBasePath()) + "`" + "<br>"
	bodyHtml += "Server Path: <b>`" + smart.EscapeHtml(GetCurrentBrowserPath(r)) + "`</b>" + " ; Internal Route Path: `" + smart.EscapeHtml(GetCurrentPath(r)) + "`" + "<br>"
	bodyHtml += "Server QueryString: `" + smart.EscapeHtml(smart.GetHttpQueryStringFromRequest(r)) + "`" + "<br>"
	bodyHtml += `<div style="margin-top:4px; margin-bottom:12px;">`
	bodyHtml += "\n"
	bodyHtml += `<img src="` + smart.EscapeHtml(assets.GetProxyLogo(proxySetDetected, false)) + `" height="64" style="margin-right:12px; cursor:help;" alt="proxy-logo" title="Proxy: ` + smart.EscapeHtml("`"+proxySetDetected+"`") + `">`
	bodyHtml += "\n"
	bodyHtml += `<img src="` + smart.EscapeHtml(assets.GetSfLogo(false)) + `" height="64" style="margin-right:12px; cursor:help;" alt="sf-logo" title="Platform: ` + smart.EscapeHtml("`"+smart.NAME+" ("+smart.DESCRIPTION+") "+smart.VERSION+"`") + `">`
	bodyHtml += "\n"
	bodyHtml += `<img src="` + smart.EscapeHtml(assets.GetGolangLogo(false)) + `" height="64" style="margin-right:12px; cursor:help;" alt="golang-logo" title="Runtime: ` + smart.EscapeHtml("`"+smart.CurrentRuntimeVersion()+"`") + `">`
	bodyHtml += "\n"
	bodyHtml += `<img src="` + smart.EscapeHtml(assets.GetOSLogo(false)) + `" height="64" style="margin-right:12px; cursor:help;" alt="os-logo" title="OS / Arch: ` + smart.EscapeHtml("`"+smart.CurrentOSName()+"`"+" / "+"`"+smart.CurrentOSArch()+"`") + `">`
	bodyHtml += "\n"
	bodyHtml += `</div>`
	bodyHtml += `<hr>`
	bodyHtml += `<div style="font-size:0.75rem; color:#CCCCDD; text-align:right;">&copy; 2023-` + smart.EscapeHtml(GetCurrentYear()) + ` unix-world.org</div>`
	response.ContentBody = assets.HtmlStandaloneTemplate(title, headHtml, bodyHtml, true)
	response.ContentFileName = "index.html"

	response.ContentDisposition = smarthttputils.DISP_TYPE_INLINE
	response.CacheExpiration = -1
	response.CacheLastModified = ""
	response.CacheControl = smarthttputils.CACHE_CONTROL_NOCACHE
	response.Headers = map[string]string{}
	response.Headers["Z-Date-Time-UTC"] = smart.DateNowIsoUtc()
	response.Cookies = nil
	response.LogMessage = ""

	return


} //end fx

-- info page (html)

var RouteHandlerStatusPage HttpHandlerFunc = func(r *http.Request, headPath string, tailPaths []string, authData smart.AuthDataStruct) (response HttpResponse) {

	defer smart.PanicHandler()

	response.StatusCode = 202
	const title string = "Service Status: Up and Running ..."
	var headHtml string = assets.HTML_META_ROBOTS_NOINDEX + "\n" + assets.HTML_CSS_STYLE_PREFER_COLOR_DARK + "\n" + "<style>" + "\n" + "div.status { text-align:center; margin:10px; cursor:help; }" + "\n" + "div.signature { background:#778899; color:#FFFFFF; font-size:2rem; font-weight:bold; text-align:center; border-radius:3px; padding:10px; margin:20px; }" + "\n" + "</style>"
	var bodyHtml string = `<div class="status"><img alt="status:svg" title="` + smart.EscapeHtml(title) + `" width="48" height="48" src="` + smart.EscapeHtml(assets.GetSvgAsset("lib/framework/img/loading-spin.svg", false)) + `"></div>` + "\n" + `<div class="signature">` + "\n" + "<pre>" + "\n" + `<i class="sfi sfi-info"></i> &nbsp; ` + smart.EscapeHtml(TheStrSignature) + " ... is running" + "\n" + smart.EscapeHtml(smart.DateNowUtc()) + "</pre>" + "\n" + "</div>"
	response.ContentBody = srvassets.HtmlServerTemplate(title, headHtml, bodyHtml, false)
	response.ContentFileName = "status.html"

	response.Headers = map[string]string{}
	response.Headers["Refresh"] = "15"

	return


} //end fx

-- status page (html)

var RouteHandlerVersionPage HttpHandlerFunc = func(r *http.Request, headPath string, tailPaths []string, authData smart.AuthDataStruct) (response HttpResponse) {

	defer smart.PanicHandler()

	response.StatusCode = 203
	json := versionStruct{
		Platform:  "`" + smart.NAME + " (" + smart.DESCRIPTION + ") " + smart.VERSION + "`",
		Server:    TheStrName,
		Version:   VERSION,
		GoVersion: smart.CurrentRuntimeVersion(),
		OsName:    smart.CurrentOSName(),
		OsArch:    smart.CurrentOSArch(),
		Copyright: SIGNATURE,
	}
	response.ContentBody = smart.JsonNoErrChkEncode(json, false, false)
	response.ContentFileName = "version.json"

	return


} //end fx

-- version page (json)

type HttpResponse

type HttpResponse struct {
	StatusCode         uint16
	ContentStream      smarthttputils.HttpStreamerFunc
	ContentBody        string
	ContentFileName    string
	ContentDisposition string
	CacheExpiration    int
	CacheLastModified  string
	CacheControl       string
	Headers            map[string]string
	Cookies            []smarthttputils.CookieData
	LogMessage         string
}

type JwtAudience

type JwtAudience struct {
	Error        error
	IpList       string
	Area         string
	Privileges   string
	Restrictions string
	Xtras        string
}

func JwtParseAudience

func JwtParseAudience(audience []string) JwtAudience

type JwtClaims

type JwtClaims struct {
	Username string `json:"usr"`
	jwt.RegisteredClaims
}

type JwtData

type JwtData struct {
	Type       string    `json:"type"`
	Size       uint64    `json:"size"`
	Token      string    `json:"token"`
	TimeNow    int64     `json:"timeNow"`
	ExpMinutes int64     `json:"expMinutes"`
	ExpAt      string    `json:"expAt"`
	PublicKey  string    `json:"publicKey,omitempty"`
	MetaInfo   JwtClaims `json:"metaInfo"`
}

func ApiAuthNewBearerTokenJwt

func ApiAuthNewBearerTokenJwt(r *http.Request, expMinutes int64, userName string, userSecKey string, allowedIpList string, clientIP string, audienceXtras string) (JwtData, error)

func JwtNew

func JwtNew(jwtSignMethod string, expirationMinutes int64, clientIP string, dom string, port string, userName string, userPrivKey string, audience []string) (JwtData, error)

type JwtTokenData

type JwtTokenData struct {
	Error    error    `json:"error,omitempty"`
	Type     string   `json:"-"`
	Algo     string   `json:"algo"`
	ID       string   `json:"serial,omitempty"`
	Issuer   string   `json:"issuer,omitempty"`
	Created  string   `json:"created,omitempty"`
	ICreated int64    `json:"-"`
	IExpires int64    `json:"-"`
	Expires  string   `json:"expires,omitempty"`
	UserName string   `json:"userName"`
	Audience []string `json:"audience"`
}

func JwtExtractData

func JwtExtractData(tokenString string) JwtTokenData

type MultiPartPostFile

type MultiPartPostFile struct {
	Error  error
	Header *multipart.FileHeader
	File   multipart.File
}

func GetPostFile

func GetPostFile(r *http.Request, key string) MultiPartPostFile

func GetPostFiles

func GetPostFiles(r *http.Request, key string) ([]MultiPartPostFile, error)

type WebAuthAccountsMethods

type WebAuthAccountsMethods struct {
	GetAuthUserRecordByUserName   WebAuthGetAuthUserRecordByUserName
	ValidateAuthUserByTokenRecord WebAuthValidateAuthUserByTokenRecord
}

func GetWebAuthAccountsMethods

func GetWebAuthAccountsMethods() WebAuthAccountsMethods

type WebAuthGetAuthUserRecordByUserName

type WebAuthGetAuthUserRecordByUserName func(user string) (error, smart.AuthUserRecord)

type WebAuthValidateAuthUserByTokenRecord

type WebAuthValidateAuthUserByTokenRecord func(user string, token string) (error, smart.AuthUserToken)

type WebdavRunOptions

type WebdavRunOptions struct {
	Enabled        bool
	SharedMode     bool
	SmartSafePaths bool
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL