package verify import ( "bytes" "encoding/base64" "encoding/json" "html/template" "image/png" "net/http" "github.com/alecthomas/chroma/formatters/html" "github.com/alecthomas/chroma/lexers" "github.com/alecthomas/chroma/styles" "github.com/boombuler/barcode" "github.com/boombuler/barcode/code128" "github.com/boombuler/barcode/qr" "edge-infra.dev/pkg/edge/iam/verify/templates" "github.com/coreos/go-oidc/v3/oidc" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v4" "golang.org/x/oauth2" ) type TokenSet struct { AccessToken *jwt.Token RefreshToken string IDToken *oidc.IDToken } func (v *Verifier) printBarcode(ctx *gin.Context) { result := &Result{ Name: "verify print barcode result", Pass: true, Barcode: BarcodeResult{}, } // discover openid config to setup a oauth2 client provider, err := oidc.NewProvider(oidc.InsecureIssuerURLContext(ctx, Issuer()), IssuerURL()) if err != nil { step := Step{Name: "setup openid provider", Pass: false} writeResult(ctx, templates.Callback, result, step) return } // verify the openid configuration var claims struct { Issuer string `json:"issuer"` TokenEndpoint string `json:"token_endpoint"` } if err := provider.Claims(&claims); err != nil { step := Step{Name: "provider has valid claims", Pass: false} writeResult(ctx, templates.Callback, result, step) return } // verify the issuer if claims.Issuer != Issuer() { // "we're not minting tokens as ourselves?" step := Step{Name: "issuer on claims is valid", Pass: false} writeResult(ctx, templates.Callback, result, step) return } // verify the token-endpoint if claims.TokenEndpoint != IssuerURL()+oauth2TokenPath { // "we're not minting tokens as ourselves?" step := Step{Name: "token endpoint is correct", Pass: false} writeResult(ctx, templates.Callback, result, step) return } // weirdly named config, but it's a client oauth2Config := oauth2.Config{ ClientID: v.ClientID, ClientSecret: v.ClientSecret, RedirectURL: v.ClientURL + verifyCallbackPath, Endpoint: provider.Endpoint(), Scopes: []string{oidc.ScopeOpenID, oidc.ScopeOfflineAccess, "profile"}, } // get the params from the query code, _ := ctx.GetQuery("code") redirectURI, _ := ctx.GetQuery("redirect_uri") failureURI, _ := ctx.GetQuery("failure_uri") // exchange the barcode code for a barcode barcodeFromCode, err := oauth2Config.Exchange(ctx, code, oauth2.SetAuthURLParam("grant_type", "barcode_code")) if err != nil { step := Step{Name: "exchange barcode code for a barcode", Pass: false} writeResult(ctx, templates.Callback, result, step) return } result.Barcode.Token = barcodeFromCode.AccessToken var bc barcode.Barcode if barcodeFromCode.TokenType == "128A" { // generate image for the code128 barcode bc, _ = code128.EncodeWithoutChecksum(barcodeFromCode.AccessToken) bc, _ = barcode.Scale(bc, 700, 150) } else { bc, _ = qr.Encode(barcodeFromCode.AccessToken, qr.L, qr.Auto) bc, _ = barcode.Scale(bc, 256, 256) } var buf bytes.Buffer _ = png.Encode(&buf, bc) result.Barcode.Image = base64.StdEncoding.EncodeToString(buf.Bytes()) //nolint:gosec result.Barcode.HTML = template.HTML(toHighlightedJSON(barcodeFromCode)) result.Barcode.RedirectURI = redirectURI result.Barcode.FailureURI = failureURI steps := make([]Step, 0) steps = append(steps, // code --> barcode Step{Name: "exchange code for barcode", Pass: barcodeFromCode.Valid()}, ) writeResult(ctx, templates.PrintBarcode, result, steps...) ctx.Redirect(http.StatusFound, redirectURI) } func toHighlightedJSON(any interface{}) string { j, _ := json.MarshalIndent(any, "", " ") lexer := lexers.Get("json") style := styles.Get("github") iterator, _ := lexer.Tokenise(nil, string(j)) var b bytes.Buffer formatter := html.New( html.WrapLongLines(true), ) _ = formatter.Format(&b, style, iterator) tokenSetFormatted := b.String() return tokenSetFormatted }