Filter Type: JWT
The JWT filter type performs JWT validation on a Bearer token present in the HTTP header. If the Bearer token JWT doesn't validate, or has insufficient scope, an RFC 6750-complaint error response with a WWW-Authenticate header is returned. The list of acceptable signing keys is loaded from a JWK Set that is loaded over HTTP, as specified in jwksURI. Only RSA and none algorithms are supported.
JWT Global Arguments
---apiVersion: getambassador.io/v2kind: Filtermetadata:name: "example-jwt-filter"namespace: "example-namespace"spec:JWT:jwksURI: "url-string" # required, unless the only validAlgorithm is "none"insecureTLS: bool # optional; default is falserenegotiateTLS: "enum-string" # optional; default is "never"validAlgorithms: # optional; default is "all supported algos except for 'none'"- "RS256"- "RS384"- "RS512"- "none"audience: "string" # optional, unless `requireAudience: true`requireAudience: bool # optional; default is falseissuer: "url-string" # optional, unless `requireIssuer: true`requireIssuer: bool # optional; default is falserequireIssuedAt: bool # optional; default is falserequireExpiresAt: bool # optional; default is falserequireNotBefore: bool # optional; default is falseinjectRequestHeaders: # optional; default is []- name: "header-name-string" # requiredvalue: "go-template-string" # requirederrorResponse: # optionalcontentType: "string" # deprecated; use 'headers' insteadrealm: "string" # optional; default is "{{.metadata.name}}.{{.metadata.namespace}}"headers: # optional; default is [{name: "Content-Type", value: "application/json"}]- name: "header-name-string" # requiredvalue: "go-template-string" # requiredbodyTemplate: "string" # optional; default is `{{ . | json "" }}`
insecureTLSdisables TLS verification for the cases whenjwksURIbegins withhttps://. This is discouraged in favor of either using plainhttp://or installing a self-signed certificate.renegotiateTLSallows a remote server to request TLS renegotiation. Accepted values are "never", "onceAsClient", and "freelyAsClient".injectRequestHeadersinjects HTTP header fields in to the request before sending it to the upstream service; where the header value can be set based on the JWT value. The value is specified as a [Gotext/template][] string, with the following data made available to it:.token.Raw→stringthe raw JWT.token.Header→map[string]interface{}the JWT header, as parsed JSON.token.Claims→map[string]interface{}the JWT claims, as parsed JSON.token.Signature→stringthe token signature.httpRequestHeader→http.Headera copy of the header of the incoming HTTP request. Any changes to.httpRequestHeader(such as by using using.httpRequestHeader.Set) have no effect. It is recommended to use.httpRequestHeader.Getinstead of treating it as a map, in order to handle capitalization correctly.
Any headers listed will override (not append to) the original request header with that name.
errorResponseallows templating the error response, overriding the default json error format. Make sure you validate and test your template, not to generate server-side errors on top of client errors.contentTypeis deprecated, and is equivalent to including aname: "Content-Type"item inheaders.realmallows specifying the realm to report in theWWW-Authenticateresponse header.headerssets extra HTTP header fields in the error response. The value is specified as a Gotext/templatestring, with the same data made available to it asbodyTemplate(below). It does not have access to thejsonfunction.bodyTemplatespecifies body of the error; specified as a [Gotext/template][] string, with the following data made available to it:.status_code→integerthe HTTP status code to be returned.httpStatus→integeran alias for.status_code(hidden from{{ . | json "" }}).message→stringthe error message string.error→errorthe raw Goerrorobject that generated.message(hidden from{{ . | json "" }}).error.ValidationError→jwt.ValidationErrorthe JWT validation error, will benilif the error is not purely JWT validation (insufficient scope, malformed or missingAuthorizationheader).request_id→stringthe Envoy request ID, for correlation (hidden from{{ . | json "" }}unless.status_codeis in the 5XX range).requestId→stringan alias for.request_id(hidden from{{ . | json "" }})
In addition to the standard functions available to Go
text/templates, there is ajsonfunction that arg2 as JSON, using the arg1 string as the starting indent level.
Note: If you are using a templating system for your YAML that also makes use of Go templating, then you will need to escape the template strings meant to be interpreted by the Ambassador Edge Stack.
JWT Path-Specific Arguments
---apiVersion: getambassador.io/v2kind: FilterPolicymetadata:name: "example-filter-policy"namespace: "example-namespace"spec:rules:- host: "*"path: "*"filters:- name: "example-jwt-filter"arguments:scopes: # optional; default is []- "scope-value-1"- "scope-value-2"
scopes: A list of OAuth scope values that Ambassador will require to be listed in thescopeclaim. In addition to the normal of thescopeclaim (a JSON string containing a space-separated list of values), the JWT Filter also accepts a JSON array of values.
Example JWT Filter
# Example results are for the JWT:## eyJhbGciOiJub25lIiwidHlwIjoiSldUIiwiZXh0cmEiOiJzbyBtdWNoIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.## To save you some time decoding that JWT:## header = {# "alg": "none",# "typ": "JWT",# "extra": "so much"# }# claims = {# "sub": "1234567890",# "name": "John Doe",# "iat": 1516239022# }---apiVersion: getambassador.io/v2kind: Filtermetadata:name: example-jwt-filternamespace: example-namespacespec:JWT:jwksURI: "https://getambassador-demo.auth0.com/.well-known/jwks.json"validAlgorithms:- "none"audience: "myapp"requireAudience: falseinjectRequestHeaders:- name: "X-Fixed-String"value: "Fixed String"# result will be "Fixed String"- name: "X-Token-String"value: "{{ .token.Raw }}"# result will be "eyJhbGciOiJub25lIiwidHlwIjoiSldUIiwiZXh0cmEiOiJzbyBtdWNoIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."- name: "X-Token-H-Alg"value: "{{ .token.Header.alg }}"# result will be "none"- name: "X-Token-H-Typ"value: "{{ .token.Header.typ }}"# result will be "JWT"- name: "X-Token-H-Extra"value: "{{ .token.Header.extra }}"# result will be "so much"- name: "X-Token-C-Sub"value: "{{ .token.Claims.sub }}"# result will be "1234567890"- name: "X-Token-C-Name"value: "{{ .token.Claims.name }}"# result will be "John Doe"- name: "X-Token-C-Iat"value: "{{ .token.Claims.iat }}"# result will be "1.516239022e+09" (don't expect JSON numbers# to always be formatted the same as input; if you care about# that, specify the formatting; see the next example)- name: "X-Token-C-Iat-Decimal"value: "{{ printf \"%.0f\" .token.Claims.iat }}"# result will be "1516239022"- name: "X-Token-S"value: "{{ .token.Signature }}"# result will be "" (since "alg: none" was used in this example JWT)- name: "X-Authorization"value: "Authenticated {{ .token.Header.typ }}; sub={{ .token.Claims.sub }}; name={{ printf \"%q\" .token.Claims.name }}"# result will be: "Authenticated JWT; sub=1234567890; name="John Doe""- name: "X-UA"value: "{{ .httpRequestHeader.Get \"User-Agent\" }}"# result will be: "curl/7.66.0" or# "Mozilla/5.0 (X11; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0"# or whatever the requesting HTTP client iserrorResponse:headers:- name: "Content-Type"value: "application/json"- name: "X-Correlation-ID"value: "{{ .httpRequestHeader.Get \"X-Correlation-ID\" }}"# Regarding the "altErrorMessage" below:# ValidationErrorExpired = 1<<4 = 16# https://godoc.org/github.com/dgrijalva/jwt-go#StandardClaimsbodyTemplate: |-{"errorMessage": {{ .message | json " " }},{{- if .error.ValidationError }}"altErrorMessage": {{ if eq .error.ValidationError.Errors 16 }}"expired"{{ else }}"invalid"{{ end }},"errorCode": {{ .error.ValidationError.Errors | json " "}},{{- end }}"httpStatus": "{{ .status_code }}","requestId": {{ .request_id | json " " }}}
Questions?
We’re here to help. If you have questions, join our Slack or contact us.