Skip to content

Instantly share code, notes, and snippets.

@knight42
Created September 16, 2022 10:17
Show Gist options
  • Save knight42/836333e9263eeb5e1178069e1cef90eb to your computer and use it in GitHub Desktop.
Save knight42/836333e9263eeb5e1178069e1cef90eb to your computer and use it in GitHub Desktop.
Envoy ext_auth demo
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
address: 127.0.0.1
port_value: 3000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
http_filters:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
transport_api_version: V3
grpc_service:
envoy_grpc:
cluster_name: ext-authz-grpc
timeout: 0.25s
failure_mode_allow: false
- name: envoy.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
tracing: {}
codec_type: "AUTO"
stat_prefix: ingress_http
route_config:
virtual_hosts:
- name: service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: service
timeout: 5s
clusters:
- name: service
connect_timeout: 5s
type: STATIC
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 8000
- name: ext-authz-grpc
connect_timeout: 0.25s
type: static
typed_extension_protocol_options:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicit_http_config:
http2_protocol_options: {}
load_assignment:
cluster_name: ext-authz-grpc
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 9000
package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"log"
"net"
"net/http"
"net/url"
"time"
auth "github.com/Authing/authing-go-sdk/lib/authentication"
"github.com/Authing/authing-go-sdk/lib/constant"
"github.com/Authing/authing-go-sdk/lib/model"
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
typev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
rpcstatus "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
type server struct {
authCli *auth.Client
}
func (s *server) Check(ctx context.Context, req *authv3.CheckRequest) (*authv3.CheckResponse, error) {
log.Println("Check")
data, _ := json.MarshalIndent(req, "", " ")
log.Println("request")
fmt.Println(string(data))
u, _ := url.ParseRequestURI(req.Attributes.Request.Http.Path)
switch u.Path {
case "/oidc/login":
return s.loginHandler(ctx, req)
case "/oidc/callback":
return s.callbackHandler(ctx, req)
case "/oidc/me":
case "/oidc/logout":
}
header := http.Header{}
header.Add("cookie", req.Attributes.Request.Http.Headers["cookie"])
httpReq := http.Request{
Header: header,
}
_, err := httpReq.Cookie("at")
if err != nil {
return &authv3.CheckResponse{
Status: &rpcstatus.Status{
Code: int32(codes.Unauthenticated),
},
HttpResponse: &authv3.CheckResponse_DeniedResponse{
DeniedResponse: &authv3.DeniedHttpResponse{
Status: &typev3.HttpStatus{
Code: typev3.StatusCode_Unauthorized,
},
Body: `Please login first`,
},
},
}, nil
}
log.Println("OK")
return &authv3.CheckResponse{
Status: &rpcstatus.Status{
Code: int32(codes.OK),
},
HttpResponse: &authv3.CheckResponse_OkResponse{
OkResponse: &authv3.OkHttpResponse{
Headers: []*corev3.HeaderValueOption{
{
Header: &corev3.HeaderValue{
Key: "x-user-id",
Value: "foo",
},
},
},
},
},
}, nil
}
func (s *server) loginHandler(ctx context.Context, req *authv3.CheckRequest) (*authv3.CheckResponse, error) {
loginURL, _ := s.authCli.BuildAuthorizeUrlByOidc(model.OidcParams{
RedirectUri: "http://localhost:3000/oidc/callback",
Scope: "openid profile email offline_access username",
})
return &authv3.CheckResponse{
Status: &rpcstatus.Status{
Code: int32(codes.Unauthenticated),
},
HttpResponse: &authv3.CheckResponse_DeniedResponse{
DeniedResponse: &authv3.DeniedHttpResponse{
Status: &typev3.HttpStatus{
Code: typev3.StatusCode_Found,
},
Headers: []*corev3.HeaderValueOption{
{
Header: &corev3.HeaderValue{
Key: "location",
Value: loginURL,
},
},
},
},
},
}, nil
}
type getTokenResponse struct {
Scope string `json:"scope"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
IDToken string `json:"id_token"`
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}
func (s *server) callbackHandler(ctx context.Context, req *authv3.CheckRequest) (*authv3.CheckResponse, error) {
u, err := url.ParseRequestURI(req.Attributes.Request.Http.Path)
if err != nil {
return nil, err
}
code := u.Query().Get("code")
resp, err := s.authCli.GetAccessTokenByCode(code)
if err != nil {
return nil, err
}
var tokens getTokenResponse
err = json.Unmarshal([]byte(resp), &tokens)
if err != nil {
return nil, err
}
if len(tokens.Error) > 0 {
return nil, errors.New(tokens.Error)
}
accessTokenCookie := http.Cookie{
Name: "at",
Value: tokens.AccessToken,
Path: "/",
MaxAge: int(time.Minute * 15 / time.Second),
}
refreshTokenCookie := http.Cookie{
Name: "rt",
Value: tokens.RefreshToken,
Path: "/refresh",
MaxAge: int(time.Hour / time.Second),
}
return &authv3.CheckResponse{
Status: &rpcstatus.Status{
Code: int32(codes.Unauthenticated),
},
HttpResponse: &authv3.CheckResponse_DeniedResponse{
DeniedResponse: &authv3.DeniedHttpResponse{
Status: &typev3.HttpStatus{
Code: typev3.StatusCode_Found,
},
Headers: []*corev3.HeaderValueOption{
{
Header: &corev3.HeaderValue{
Key: "location",
Value: "/",
},
},
{
Header: &corev3.HeaderValue{
Key: "set-cookie",
Value: accessTokenCookie.String(),
},
},
{
Header: &corev3.HeaderValue{
Key: "set-cookie",
Value: refreshTokenCookie.String(),
},
},
},
},
},
}, nil
}
func main() {
var (
appID, appSecret, oidcHost string
)
flag.StringVar(&appID, "app-id", "", "App id")
flag.StringVar(&appSecret, "app-secret", "", "App secret")
flag.StringVar(&oidcHost, "host", "", "OIDC host")
authCli := auth.NewClient(appID, appSecret, oidcHost)
authCli.Protocol = constant.OIDC
authCli.TokenEndPointAuthMethod = constant.ClientSecretPost
authCli.RedirectUri = "http://localhost:3000/oidc/callback"
addr := ":9000"
lis, err := net.Listen("tcp", addr)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
authv3.RegisterAuthorizationServer(s, &server{
authCli: authCli,
})
log.Printf("Start server on %s", addr)
s.Serve(lis)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment