Overview
isAuthenticatedSE
and authenticateSE
enable you to implement bespoke authentication mechanisms not covered by default providers. These service extensions can be used for Proxy, SAML, and OIDC apps, and they must be used together.
isAuthenticatedSE
can be used to override the default behavior that determines if a user is already authenticated. authenticateSE
is then used to take control of how the authentication will be done.
Implementation
To create isAuthenticatedSE
and authenticateSE
go to Service Extensions.
From the right sidebar, click the + icon next to Authentication.
Enter a name and description for the service extension, and click Create.
The service extension code appears on the next page. From here, you can add assets, providers, claims, and/or metadata. When you've finished adding this information, click Update.
You will receive a confirmation message in the lower right corner that the service extension file has been updated.
You can now use this service extension in an existing or new user flow. For SAML and OIDC app user flows, the service extension can be selected as an Authentication Provider:

Click to enlarge
For Proxy app user flows, the service extension will appear as an Authentication Provider that can be selected when configuring your access control policies for a specific resource location.
Code Example
/etc/maverics/extensions/auth.go
package main
import (
"net/http"
"errors"
"crypto/sha256"
"encoding/hex"
"github.com/strata-io/service-extension/orchestrator"
)
func IsAuthenticated(api orchestrator.Orchestrator, _ http.ResponseWriter, _ *http.Request) bool {
logger := api.Logger()
logger.Debug("se", "determining if user is authenticated")
session, err := api.Session()
if err != nil {
logger.Error("se", "unable to retrieve session", "error", err.Error())
return false
}
isOktaAuth, err := session.GetString("okta.authenticated")
if err != nil {
logger.Error("se", "unable to retrieve session value", "error", err.Error())
return false
}
if isOktaAuth == "true" {
return true
}
return false
}
func Authenticate(api orchestrator.Orchestrator, rw http.ResponseWriter, req *http.Request) {
logger := api.Logger()
logger.Debug("se", "authenticating user")
oktaIDP, err := api.IdentityProvider("okta")
if err != nil {
logger.Error(
"se", "failed to retrieve Okta IDP",
"error", err.Error(),
)
http.Error(
rw,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError,
)
return
}
oktaIDP.Login(rw, req)
}
func LoadAttributes(api orchestrator.Orchestrator, _ http.ResponseWriter, _ *http.Request) error {
logger := api.Logger()
logger.Debug("se", "loading custom attributes from LDAP")
session, err := api.Session()
if err != nil {
logger.Error("se", "unable to retrieve session", "error", err.Error())
return err
}
mail, err := session.GetString("okta.email")
if err != nil {
return fmt.Errorf("failed to find user email required for LDAP query: %w", err)
}
ldap, err := api.AttributeProvider("ldap")
if err != nil {
return fmt.Errorf("failed to find LDAP attribute provider")
}
attrs, err := ldap.Query(mail, []string{"givenname", "sn", "mobile"})
if err != nil {
return fmt.Errorf("failed to query LDAP: %w", err)
}
for k, v := range attrs {
logger.Debug(
"se", "setting LDAP attribute on session",
"attribute", k,
"value", v,
)
_ = session.SetString(k, v)
}
err = session.Save()
if err != nil {
return fmt.Errorf("unable to save session state: %w", err)
}
return nil
}
func BackchannelAuthenticate(api orchestrator.Orchestrator, req *http.Request) error {
logger := api.Logger()
logger.Debug("se", "authenticating user in backchannel flow")
username := req.Form.Get("username")
password := req.Form.Get("password")
secrets, err := api.SecretProvider()
if err != nil {
logger.Error(
"se", "failed to retrieve secret provider",
"error", err.Error(),
)
return errors.New("failed to retrieve secret provider")
}
pwdHash := secrets.GetString(username)
if len(pwdHash) == 0 {
return errors.New("invalid user credentials")
}
hash := sha256.Sum256([]byte(password))
hashHex := hex.EncodeToString(hash[:])
if pwdHash != hashHex {
return errors.New("invalid user credentials")
}
return nil
}
func IsAuthorized(api orchestrator.Orchestrator, _ http.ResponseWriter, _ *http.Request) bool {
logger := api.Logger()
logger.Debug("se", "determining if user is authorized")
session, err := api.Session()
if err != nil {
logger.Error("se", "unable to retrieve session", "error", err.Error())
return false
}
rawGroups, _ := session.GetString("okta.groups")
groups := strings.Split(rawGroups, ",")
for _, group := range groups {
if group == "executives" {
return true
}
}
return false
}
func BuildAccessTokenClaims(api orchestrator.Orchestrator, _ *http.Request) (map[string]any, error) {
logger := api.Logger()
logger.Debug("se", "building access token claims")
session, err := api.Session()
if err != nil {
logger.Error("se", "unable to retrieve session", "error", err.Error())
return nil, err
}
roles, err := session.GetString("okta.roles")
return map[string]any{
"scope": roles,
}, err
}
func BuildIDTokenClaims(api orchestrator.Orchestrator, _ *http.Request) (map[string]any, error) {
logger := api.Logger()
logger.Debug("se", "building ID token claims")
session, err := api.Session()
if err != nil {
logger.Error("se", "unable to retrieve session", "error", err.Error())
return nil, err
}
groups, err := session.GetString("okta.groups")
return map[string]any{
"groups": groups,
}, err
}