Created
September 16, 2022 10:17
-
-
Save knight42/836333e9263eeb5e1178069e1cef90eb to your computer and use it in GitHub Desktop.
Envoy ext_auth demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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