Files
upstream-mattermost/server/public/model/saml_relay_state.go
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

79 lines
2.4 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package model
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"maps"
"strconv"
"strings"
)
// SamlRelayStateExpiryTime bounds how long a signed RelayState is valid for. It only needs to
// survive a single SAML login round-trip, so this is intentionally short.
const SamlRelayStateExpiryTime = 1000 * 60 * 5 // 5 minutes
const samlRelayStateExpKey = "exp"
// SignSamlRelayState signs relayProps with key and returns an opaque, tamper-evident RelayState
// string safe to round-trip through the IdP. relayProps itself is not modified.
func SignSamlRelayState(key []byte, relayProps map[string]string) string {
signed := make(map[string]string, len(relayProps)+1)
maps.Copy(signed, relayProps)
signed[samlRelayStateExpKey] = strconv.FormatInt(GetMillis()+SamlRelayStateExpiryTime, 10)
payload := []byte(MapToJSON(signed))
mac := hmac.New(sha256.New, key)
mac.Write(payload)
return base64.RawURLEncoding.EncodeToString(payload) + "." + base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
// VerifySamlRelayState verifies a RelayState produced by SignSamlRelayState. It returns the
// original relayProps (with the expiry field stripped) if, and only if, the signature is valid
// and the embedded expiry has not passed.
func VerifySamlRelayState(key []byte, relayState string) (map[string]string, error) {
payloadPart, sigPart, ok := strings.Cut(relayState, ".")
if !ok {
return nil, errors.New("malformed relay state")
}
payload, err := base64.RawURLEncoding.DecodeString(payloadPart)
if err != nil {
return nil, errors.New("malformed relay state payload")
}
sig, err := base64.RawURLEncoding.DecodeString(sigPart)
if err != nil {
return nil, errors.New("malformed relay state signature")
}
mac := hmac.New(sha256.New, key)
mac.Write(payload)
if !hmac.Equal(sig, mac.Sum(nil)) {
return nil, errors.New("invalid relay state signature")
}
relayProps := MapFromJSON(bytes.NewReader(payload))
expStr, ok := relayProps[samlRelayStateExpKey]
if !ok {
return nil, errors.New("relay state missing expiry")
}
exp, err := strconv.ParseInt(expStr, 10, 64)
if err != nil {
return nil, errors.New("relay state has invalid expiry")
}
if GetMillis() > exp {
return nil, errors.New("relay state expired")
}
delete(relayProps, samlRelayStateExpKey)
return relayProps, nil
}