HTTP

Client tokens can be presented via HTTP requests in two different valid ways. This choice allows us to support two different types of workloads.

1. Authorization Header

For applications that communicate with Flipt over HTTP, the Authorization header is most appropriate. It must be provided in the form Authorization: Bearer <client_token>.

The following examples illustrate this in the context of various programming languages:

import (
  "context"
  "net/http"
)

func main() {
  req := http.NewRequest("GET", "https://flipt.your.instance/api/v1/flags", nil)
  req.Header.Set("Authorization", "Bearer gt6P_zIqTnCngfHDCpWb48ob5EBt3PqunUhpofNCNnc=")

  resp, err := http.Do(req)
  // ...
}

It’s important to enable CSRF prevention in your Flipt configuration when using a “session compatible” authentication method and Cookie based authentication in the browser.

For browser-based applications (e.g. Flipt’s own user interface) we support supplying a client token via a particular Cookie called flipt_client_token.

import (
  "context"
  "net/http"
)

func main() {
  req := http.NewRequest("GET", "https://flipt.your.instance/api/v1/flags", nil)
  req.AddCookie(&http.Cookie{
    Name: "flipt_client_token",
    Value: "gt6P_zIqTnCngfHDCpWb48ob5EBt3PqunUhpofNCNnc=",
    }
  )

  resp, err := http.Do(req)
  // ...
}

This allows for stateful browser sessions to be established.

When using a “session compatible” authentication method (e.g. OIDC), Flipt will automatically establish this cookie via a Set-Cookie response header during the authentication method exchange.

In a browser context this means subsequent API calls will be automatically authenticated given the API requests are invoked with credentials included (cookies are enabled). Flipt’s UI leverages this mechanism for its login functionality.

GRPC

For gRPC we use the Metadata functionality similar to HTTP Headers. The lower-case authorization metadata key should be supplied with a single string Bearer <client-token> to any RPC calls.

Example

The following example authenticates a single gRPC client request:

rpc.go
func DoRequest(ctx context.Context, flagKey string) {
  ctx := metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer gt6P_zIqTnCngfHDCpWb48ob5EBt3PqunUhpofNCNnc=")

  flag, err := flipt.GetFlags(ctx, &flipt.GetFlagRequest{
    Key: flagKey,
  })

  //...
}

This subsequent example demonstrates using a client unary interceptor, which authenticates all outgoing requests:

interceptor.go
func AuthUnaryClientInterceptor(optFuncs ...CallOption) grpc.UnaryClientInterceptor {
    return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
        ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer gt6P_zIqTnCngfHDCpWb48ob5EBt3PqunUhpofNCNnc=")
        return invoker(ctx, method, req, reply, cc, opts...)
	}
}