Overview
Authentication in Serverless Workflow specifies the method by which the workflow accesses protected resources or services. The DSL supports a comprehensive suite of standard authentication mechanisms that can be defined globally or associated with specific endpoints.
External resources, function calls, and HTTP endpoints may all define authentication to ensure secure access to protected services.
Authentication Mechanisms
The Serverless Workflow DSL supports several standard authentication schemes:
Basic Authentication
Utilizes a username-password pair for authentication:
use:
authentications:
myBasicAuth:
basic:
username: admin
password: secret123
The username for basic authentication
The password for basic authentication
Using Basic Authentication
do:
- callProtectedApi:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/protected
authentication: myBasicAuth
Inline Basic Authentication
do:
- callWithInlineAuth:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/data
authentication:
basic:
username: user
password: pass
Bearer Token Authentication
Uses a bearer token for authentication:
use:
authentications:
myBearerAuth:
bearer: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
The bearer token for authentication
Using Bearer Authentication
do:
- callApiWithToken:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/secure
authentication: myBearerAuth
Dynamic Bearer Token
use:
authentications:
dynamicBearer:
bearer: ${ .userToken }
do:
- callWithUserToken:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/user-data
authentication: dynamicBearer
Bearer Token from Secrets
use:
secrets:
- apiToken
authentications:
secretBearer:
bearer: ${ $secrets.apiToken }
do:
- secureCall:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/data
authentication: secretBearer
Use $secrets with caution when defining authentication. Ensure secrets are properly secured and not inadvertently exposed in logs or outputs.
OAuth2 Authentication
Implements OAuth2 authorization framework for secure access:
use:
authentications:
myOAuth2:
oauth2:
authority: http://keycloak/realms/fake-authority/.well-known/openid-configuration
grant: client-credentials
client:
id: workflow-runtime
secret: workflow-runtime-client-secret
scopes: [api, read, write]
audiences: [runtime]
The OAuth2 authorization server endpoint (OIDC discovery endpoint)
The OAuth2 grant type (e.g., client-credentials, password, authorization-code)
OAuth2 client credentials
The OAuth2 client identifier
Array of requested OAuth2 scopes
Array of intended audiences for the token
Client Credentials Flow
use:
authentications:
serviceAuth:
oauth2:
authority: https://auth.example.com/.well-known/openid-configuration
grant: client-credentials
client:
id: my-service-client
secret: my-service-secret
scopes: [api.read, api.write]
do:
- callMicroservice:
call: http
with:
method: post
endpoint:
uri: https://microservice.example.com/api
authentication: serviceAuth
body: ${ .requestData }
Password Grant Flow
use:
authentications:
userAuth:
oauth2:
authority: https://auth.example.com/.well-known/openid-configuration
grant: password
client:
id: user-client
secret: user-client-secret
username: ${ .username }
password: ${ .password }
scopes: [profile, email]
do:
- getUserProfile:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/profile
authentication: userAuth
Authorization Code Flow
use:
authentications:
webAuth:
oauth2:
authority: https://auth.example.com/.well-known/openid-configuration
grant: authorization-code
client:
id: web-app-client
secret: web-app-secret
redirectUri: https://myapp.example.com/callback
scopes: [openid, profile, email]
do:
- accessProtectedResource:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/protected-resource
authentication: webAuth
Custom Authentication Schemes
While the DSL provides standard authentication mechanisms, you can also implement custom authentication:
use:
authentications:
customAuth:
# Custom authentication properties
scheme: custom
properties:
apiKey: ${ $secrets.customApiKey }
signature: ${ .computedSignature }
Authentication Policies
Authentication policies can be defined globally and referenced by name throughout the workflow:
Defining Authentication Policies
use:
authentications:
apiKeyAuth:
bearer: ${ $secrets.apiKey }
serviceAuth:
oauth2:
authority: https://auth.example.com/.well-known/openid-configuration
grant: client-credentials
client:
id: service-client
secret: ${ $secrets.serviceClientSecret }
scopes: [api]
adminAuth:
basic:
username: admin
password: ${ $secrets.adminPassword }
Referencing Authentication Policies
do:
- callPublicApi:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/public
authentication: apiKeyAuth
- callServiceApi:
call: http
with:
method: get
endpoint:
uri: https://service.example.com/data
authentication: serviceAuth
- callAdminApi:
call: http
with:
method: get
endpoint:
uri: https://admin.example.com/config
authentication: adminAuth
Authentication with External Resources
External resources can specify authentication for secure access:
use:
resources:
protectedDatabase:
endpoint:
uri: https://database.example.com
authentication:
basic:
username: dbuser
password: ${ $secrets.dbPassword }
secureApi:
endpoint:
uri: https://secure-api.example.com
authentication:
bearer: ${ $secrets.apiToken }
Authentication Patterns
Pattern: Multi-Tier Authentication
use:
secrets:
- tier1Token
- tier2Token
- tier3Token
authentications:
tier1Auth:
bearer: ${ $secrets.tier1Token }
tier2Auth:
bearer: ${ $secrets.tier2Token }
tier3Auth:
bearer: ${ $secrets.tier3Token }
do:
- callTier1:
call: http
with:
method: get
endpoint:
uri: https://tier1.example.com/api
authentication: tier1Auth
- callTier2:
if: ${ .callTier1.output.needsTier2 }
call: http
with:
method: get
endpoint:
uri: https://tier2.example.com/api
authentication: tier2Auth
- callTier3:
if: ${ .callTier2.output.needsTier3 }
call: http
with:
method: get
endpoint:
uri: https://tier3.example.com/api
authentication: tier3Auth
Pattern: Token Refresh
do:
- getAccessToken:
call: http
with:
method: post
endpoint:
uri: https://auth.example.com/token
body:
grant_type: client_credentials
client_id: ${ $secrets.clientId }
client_secret: ${ $secrets.clientSecret }
export:
as: ${ $context + { accessToken: $output.body.access_token } }
- callApiWithToken:
try:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/data
authentication:
bearer: ${ $context.accessToken }
catch:
errors:
with:
status: 401
do:
- refreshToken:
call: http
with:
method: post
endpoint:
uri: https://auth.example.com/token
body:
grant_type: refresh_token
refresh_token: ${ $context.refreshToken }
export:
as: ${ $context + { accessToken: $output.body.access_token } }
- retryCall:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/data
authentication:
bearer: ${ $context.accessToken }
Pattern: Service-to-Service Authentication
use:
authentications:
serviceA:
oauth2:
authority: https://auth.example.com/.well-known/openid-configuration
grant: client-credentials
client:
id: service-a-client
secret: ${ $secrets.serviceASecret }
scopes: [service-a-api]
serviceB:
oauth2:
authority: https://auth.example.com/.well-known/openid-configuration
grant: client-credentials
client:
id: service-b-client
secret: ${ $secrets.serviceBSecret }
scopes: [service-b-api]
do:
- callServiceA:
call: http
with:
method: get
endpoint:
uri: https://service-a.example.com/data
authentication: serviceA
- callServiceB:
call: http
with:
method: post
endpoint:
uri: https://service-b.example.com/process
authentication: serviceB
body: ${ .callServiceA.output }
Pattern: Conditional Authentication
use:
authentications:
publicAuth:
bearer: ${ $secrets.publicToken }
premiumAuth:
bearer: ${ $secrets.premiumToken }
do:
- determineAuthLevel:
call: userService
with:
userId: ${ .userId }
- callApiWithAppropriateAuth:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/data
authentication: ${ if .determineAuthLevel.output.isPremium then "premiumAuth" else "publicAuth" end }
Pattern: Federated Authentication
use:
authentications:
providerA:
oauth2:
authority: https://provider-a.example.com/.well-known/openid-configuration
grant: authorization-code
client:
id: client-a
secret: ${ $secrets.providerASecret }
providerB:
oauth2:
authority: https://provider-b.example.com/.well-known/openid-configuration
grant: authorization-code
client:
id: client-b
secret: ${ $secrets.providerBSecret }
do:
- selectProvider:
switch:
- when: ${ .authProvider == "provider-a" }
then: authenticateWithA
- when: ${ .authProvider == "provider-b" }
then: authenticateWithB
- authenticateWithA:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/resource
authentication: providerA
then: accessResource
- authenticateWithB:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/resource
authentication: providerB
then: accessResource
- accessResource:
call: processResource
Authorization Descriptor
The $authorization runtime expression argument provides access to resolved authorization information:
do:
- callAuthenticatedApi:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/data
authentication: myAuth
- logAuthInfo:
call: logger
with:
scheme: ${ $authorization.scheme } # e.g., "Bearer"
parameter: ${ $authorization.parameter } # e.g., token value
The resolved authorization scheme (e.g., “Bearer”, “Basic”)
The resolved authorization parameter (e.g., token, encoded credentials)
Be cautious when logging or outputting $authorization.parameter as it contains sensitive credentials.
Security Best Practices
Use secrets for sensitive data
Store all passwords, tokens, and keys in secrets rather than hardcoding them in workflow definitions.
Reference authentication policies by name
Define authentication policies globally and reference them by name to avoid duplication and improve maintainability.
Use appropriate authentication schemes
Choose OAuth2 for service-to-service communication, bearer tokens for API access, and basic auth only for simple internal services.
Implement token refresh
Handle token expiration gracefully by implementing refresh logic when using OAuth2 or other token-based authentication.
Minimize scope and privileges
Request only the minimum scopes and privileges necessary for the workflow to perform its tasks.
Rotate credentials regularly
Implement a process for rotating secrets and updating authentication policies without workflow downtime.
Never log credentials
Avoid logging authentication parameters or secrets. Use secure logging practices and sanitize outputs.
Common Authentication Scenarios
Scenario: Calling Third-Party APIs
use:
secrets:
- stripeApiKey
- twilioAuthToken
authentications:
stripeAuth:
bearer: ${ $secrets.stripeApiKey }
twilioAuth:
basic:
username: ${ $secrets.twilioAccountSid }
password: ${ $secrets.twilioAuthToken }
do:
- processPayment:
call: http
with:
method: post
endpoint:
uri: https://api.stripe.com/v1/charges
authentication: stripeAuth
body: ${ .paymentData }
- sendSmsNotification:
call: http
with:
method: post
endpoint:
uri: https://api.twilio.com/2010-04-01/Accounts/${ $secrets.twilioAccountSid }/Messages.json
authentication: twilioAuth
body:
To: ${ .phoneNumber }
Body: Payment processed successfully
Scenario: Microservices Communication
use:
authentications:
internalAuth:
oauth2:
authority: https://internal-auth.example.com/.well-known/openid-configuration
grant: client-credentials
client:
id: orchestrator-service
secret: ${ $secrets.orchestratorSecret }
scopes: [internal-api]
audiences: [user-service, order-service, inventory-service]
do:
- getUserData:
call: http
with:
method: get
endpoint:
uri: https://user-service/api/users/${ .userId }
authentication: internalAuth
- getOrderHistory:
call: http
with:
method: get
endpoint:
uri: https://order-service/api/orders?userId=${ .userId }
authentication: internalAuth
- checkInventory:
call: http
with:
method: post
endpoint:
uri: https://inventory-service/api/check
authentication: internalAuth
body: ${ .getOrderHistory.output }
Scenario: Multi-Environment Configuration
use:
secrets:
- devApiToken
- stagingApiToken
- prodApiToken
authentications:
devAuth:
bearer: ${ $secrets.devApiToken }
stagingAuth:
bearer: ${ $secrets.stagingApiToken }
prodAuth:
bearer: ${ $secrets.prodApiToken }
do:
- determineEnvironment:
set:
env: ${ $runtime.metadata.environment }
- callApiWithEnvAuth:
call: http
with:
method: get
endpoint:
uri: ${ "https://api-" + .env + ".example.com/data" }
authentication: ${ .env + "Auth" }
Troubleshooting Authentication
Handling Authentication Failures
do:
- tryAuthenticatedCall:
try:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/data
authentication: myAuth
catch:
errors:
with:
status: 401
as: authError
do:
- logAuthFailure:
call: logger
with:
error: ${ .authError }
message: Authentication failed
- notifyAdmin:
call: notificationService
with:
message: Authentication failure detected
details: ${ .authError }
Testing Authentication
do:
- testAuthentication:
try:
call: http
with:
method: get
endpoint:
uri: https://api.example.com/auth/test
authentication: myAuth
catch:
errors: {}
as: testError
do:
- logTestResult:
call: logger
with:
success: false
error: ${ .testError }
- logSuccess:
if: ${ .testError == null }
call: logger
with:
success: true
message: Authentication test passed