Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 58c0b9c

Browse files
committed
allow application to create ziti context without credentials and then query for external providers
simplify using external JWT for authentication
1 parent 0351630 commit 58c0b9c

File tree

4 files changed

+171
-5
lines changed

4 files changed

+171
-5
lines changed

edge-apis/clients.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@ package edge_apis
1818

1919
import (
2020
"crypto/x509"
21+
"net/http"
22+
"net/url"
23+
"strings"
24+
"sync/atomic"
25+
2126
"github.com/go-openapi/runtime"
2227
openapiclient "github.com/go-openapi/runtime/client"
2328
"github.com/go-openapi/strfmt"
2429
"github.com/michaelquigley/pfxlog"
2530
"github.com/openziti/edge-api/rest_client_api_client"
2631
"github.com/openziti/edge-api/rest_management_api_client"
27-
"net/http"
28-
"net/url"
29-
"strings"
30-
"sync/atomic"
3132
)
3233

3334
// ApiType is an interface constraint for generics. The underlying go-swagger types only have fields, which are
@@ -173,7 +174,7 @@ func (self *BaseClient[A]) ProcessControllers(authEnabledApi AuthEnabledApi) {
173174
list, err := authEnabledApi.ListControllers()
174175

175176
if err != nil {
176-
pfxlog.Logger().WithError(err).Error("error listing controllers, continuing with 1 default configured controller")
177+
pfxlog.Logger().WithError(err).Debug("error listing controllers, continuing with 1 default configured controller")
177178
return
178179
}
179180

example/device-auth/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
OIDC Device Code authentication example
2+
---
3+
4+
This sample shows OpenZiti OIDC authentication with device code flow.
5+
Prerequisites:
6+
- your OpenZiti network is configured with an external OIDC provider
7+
- your OIDC provider is configured to allow device code flow

example/device-auth/main.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"flag"
6+
"fmt"
7+
"log"
8+
"net/http"
9+
"net/url"
10+
"strings"
11+
"time"
12+
13+
"github.com/dgrijalva/jwt-go"
14+
"github.com/openziti/edge-api/rest_model"
15+
"github.com/openziti/edge-api/rest_util"
16+
nfx509 "github.com/openziti/foundation/v2/x509"
17+
"github.com/openziti/sdk-golang/ziti"
18+
"gopkg.in/square/go-jose.v2/json"
19+
)
20+
21+
func die[T interface{}](res T, err error) T {
22+
if err != nil {
23+
log.Fatal(err)
24+
}
25+
return res
26+
}
27+
28+
func main() {
29+
cfg := flag.String("config", "", "path to config file")
30+
openzitiURL := flag.String("ziti", "https://localhost:1280", "URL of the OpenZiti service")
31+
flag.Parse()
32+
33+
var config *ziti.Config
34+
if cfg == nil || *cfg == "" {
35+
config = &ziti.Config{
36+
ZtAPI: *openzitiURL,
37+
}
38+
// warning: this call is insecure and should not be used in production
39+
ca := die(rest_util.GetControllerWellKnownCas(*openzitiURL))
40+
var buf bytes.Buffer
41+
_ = nfx509.MarshalToPem(ca, &buf)
42+
config.ID.CA = buf.String()
43+
} else {
44+
if openzitiURL == nil || *openzitiURL == "" {
45+
log.Fatal("OpenZiti URL must be specified")
46+
}
47+
config = die(ziti.NewConfigFromFile(*cfg))
48+
}
49+
ztx := die(ziti.NewContext(config))
50+
51+
err := ztx.Authenticate()
52+
var provider *rest_model.ClientExternalJWTSignerDetail
53+
if err != nil {
54+
fmt.Println("Try authenticating with external provider")
55+
idps := die(ztx.GetExternalSigners())
56+
for idx, idp := range idps {
57+
fmt.Printf("%d: %s\n", idx, *idp.Name)
58+
}
59+
60+
fmt.Printf("Select provider allowing device code flow.\nEnter number[0-%d] to authenticate: ", len(idps)-1)
61+
var id int
62+
_ = die(fmt.Scanf("%d", &id))
63+
64+
provider = idps[id]
65+
}
66+
if provider == nil {
67+
log.Fatal("No provider found")
68+
}
69+
fmt.Printf("Using %s\n", *provider.Name)
70+
71+
resp := die(http.Get(*provider.ExternalAuthURL + "/.well-known/openid-configuration"))
72+
var oidcConfig map[string]interface{}
73+
_ = json.NewDecoder(resp.Body).Decode(&oidcConfig)
74+
75+
deviceAuth := oidcConfig["device_authorization_endpoint"].(string)
76+
scopes := append(provider.Scopes, "openid")
77+
ss := strings.Join(scopes, " ")
78+
resp = die(http.PostForm(deviceAuth, url.Values{
79+
"client_id": {*provider.ClientID},
80+
"scope": {ss},
81+
"audience": {*provider.Audience},
82+
}))
83+
84+
var deviceCode map[string]interface{}
85+
_ = json.NewDecoder(resp.Body).Decode(&deviceCode)
86+
if completeUrl, ok := deviceCode["verification_uri_complete"]; ok {
87+
fmt.Printf("Open %s in your browser\n", completeUrl.(string))
88+
} else if verifyUrl, ok := deviceCode["verification_uri"]; ok {
89+
fmt.Printf("Open %s in your browser, and use code %s\n",
90+
verifyUrl.(string), deviceCode["user_code"].(string))
91+
} else {
92+
log.Fatal("Unable to determine verification URL")
93+
}
94+
95+
interval := time.Duration(int(deviceCode["interval"].(float64))) * time.Second
96+
97+
var token map[string]interface{}
98+
for {
99+
clear(token)
100+
time.Sleep(interval)
101+
102+
tokenUrl := oidcConfig["token_endpoint"].(string)
103+
resp = die(http.PostForm(tokenUrl, url.Values{
104+
"client_id": {*provider.ClientID},
105+
"device_code": {deviceCode["device_code"].(string)},
106+
"grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},
107+
}))
108+
109+
json.NewDecoder(resp.Body).Decode(&token)
110+
errmsg, hasErr := token["error"]
111+
if !hasErr {
112+
break
113+
}
114+
errormsg := errmsg.(string)
115+
if errormsg == "authorization_pending" {
116+
fmt.Println("Waiting for user to authorize...")
117+
continue
118+
}
119+
log.Fatal(errormsg)
120+
}
121+
122+
accessToken := token["access_token"].(string)
123+
tok, _ := jwt.Parse(accessToken, nil)
124+
if claims, ok := tok.Claims.(jwt.MapClaims); ok {
125+
for k, v := range claims {
126+
fmt.Printf("\t%s: %v\n", k, v)
127+
}
128+
}
129+
ztx.LoginWithJWT(accessToken)
130+
131+
err = ztx.Authenticate()
132+
if err != nil {
133+
log.Fatal(err)
134+
}
135+
fmt.Println("Authenticated")
136+
137+
services, _ := ztx.GetServices()
138+
fmt.Println("Available Services:")
139+
for _, svc := range services {
140+
fmt.Printf("\t%s\n", *svc.Name)
141+
}
142+
}

ziti/ziti.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ type Context interface {
9595
// SetCredentials sets the credentials used to authenticate against the Edge Client API.
9696
SetCredentials(authenticator apis.Credentials)
9797

98+
LoginWithJWT(jst string)
99+
98100
// GetCredentials returns the currently set credentials used to authenticate against the Edge Client API.
99101
GetCredentials() apis.Credentials
100102

@@ -486,6 +488,20 @@ func (context *ContextImpl) SetCredentials(credentials apis.Credentials) {
486488
context.CtrlClt.Credentials = credentials
487489
}
488490

491+
func (context *ContextImpl) LoginWithJWT(jwt string) {
492+
cred := context.CtrlClt.Credentials
493+
jwtCred := &apis.JwtCredentials{
494+
BaseCredentials: apis.BaseCredentials{
495+
ConfigTypes: cred.Payload().ConfigTypes,
496+
EnvInfo: cred.Payload().EnvInfo,
497+
SdkInfo: cred.Payload().SdkInfo,
498+
CaPool: context.CtrlClt.CaPool,
499+
},
500+
JWT: jwt,
501+
}
502+
context.SetCredentials(jwtCred)
503+
}
504+
489505
func (context *ContextImpl) GetCredentials() apis.Credentials {
490506
return context.CtrlClt.Credentials
491507
}

0 commit comments

Comments
 (0)