Skip to content

Commit 450d2ca

Browse files
authored
JARM (#30)
1 parent f1eee46 commit 450d2ca

File tree

16 files changed

+267
-87
lines changed

16 files changed

+267
-87
lines changed

README.md

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* A simple and intuitive interface for quickly trying out different OAuth 2.0 grant types and client authentication methods
1515
* Supports all modern OAuth 2.0 grant types: authorization code, implicit, password, client credentials, refresh token, JWT bearer, token exchange
1616
* Supports all client authentication methods: client secret basic, client secret post, client secret JWT, private key JWT, TLS client auth
17+
* Supports the following extensions: PKCE, JARM
1718

1819
## Installation
1920

@@ -119,32 +120,6 @@ oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
119120

120121
[Learn more about authorization code flow](https://cloudentity.com/developers/basics/oauth-grant-types/authorization-code-flow/)
121122

122-
#### Authorization code + PKCE
123-
124-
The Proof Key for Code Exchange (PKCE) is an extension to the OAuth2 authorization code grant flow that
125-
provides additional security when authenticating with an OAuth2 provider. In the PKCE flow, the client
126-
generates a code verifier and a code challenge, which are then sent to the OAuth2 provider during
127-
the authorization request. The provider returns an authorization code, which the client then exchanges for
128-
an access token along with the code verifier. The provider verifies the code verifier to ensure that the
129-
request is coming from the same client that initiated the authorization request.
130-
131-
This additional step helps to prevent attackers from intercepting the authorization code and using it to
132-
obtain an access token. PKCE is recommended for all public clients, such as single-page or mobile
133-
applications, where the client secret cannot be securely stored.
134-
135-
``` sh
136-
oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
137-
--client-id db5e375e7b634095b24bbb683fcb955b \
138-
--response-types code \
139-
--response-mode query \
140-
--grant-type authorization_code \
141-
--auth-method none \
142-
--scopes openid,email \
143-
--pkce
144-
```
145-
146-
[Learn more about authorization code flow with pkce](https://cloudentity.com/developers/basics/oauth-grant-types/authorization-code-with-pkce/)
147-
148123
#### Implicit
149124

150125
This grant type is similar to the authorization code grant, but the access token is returned directly to
@@ -424,6 +399,71 @@ oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
424399

425400
[Lean more about none with PKCE](https://cloudentity.com/developers/basics/oauth-client-authentication/client-auth-set-to-none-with-pkce/)
426401

402+
### Extensions
403+
404+
#### PKCE
405+
406+
The Proof Key for Code Exchange (PKCE) is an extension to the OAuth2 authorization code grant flow that
407+
provides additional security when authenticating with an OAuth2 provider. In the PKCE flow, the client
408+
generates a code verifier and a code challenge, which are then sent to the OAuth2 provider during
409+
the authorization request. The provider returns an authorization code, which the client then exchanges for
410+
an access token along with the code verifier. The provider verifies the code verifier to ensure that the
411+
request is coming from the same client that initiated the authorization request.
412+
413+
This additional step helps to prevent attackers from intercepting the authorization code and using it to
414+
obtain an access token. PKCE is recommended for all public clients, such as single-page or mobile
415+
applications, where the client secret cannot be securely stored.
416+
417+
``` sh
418+
oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
419+
--client-id db5e375e7b634095b24bbb683fcb955b \
420+
--response-types code \
421+
--response-mode query \
422+
--grant-type authorization_code \
423+
--auth-method none \
424+
--scopes openid,email \
425+
--pkce
426+
```
427+
428+
[Learn more about authorization code flow with pkce](https://cloudentity.com/developers/basics/oauth-grant-types/authorization-code-with-pkce/)
429+
430+
#### JARM
431+
432+
JWT-secured OAuth 2.0 authorization response, also known as JARM, is a method of securely transmitting authorization
433+
information in an OAuth 2.0 authorization response using JSON Web Tokens (JWT). This allows the authorization response
434+
to be verified by the client, ensuring that the information is coming from a trusted source and has not been tampered
435+
with. The JWT is signed using a secret key shared between the authorization server and the client, allowing the client
436+
to verify the authenticity of the JWT. This provides an additional layer of security to the OAuth 2.0 authorization process.
437+
438+
##### Signed JWT
439+
440+
``` sh
441+
oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
442+
--client-id cauktionbud6q8ftlqq0 \
443+
--client-secret HCwQ5uuUWBRHd04ivjX5Kl0Rz8zxMOekeLtqzki0GPc \
444+
--response-types code \
445+
--response-mode query.jwt \
446+
--grant-type authorization_code \
447+
--auth-method client_secret_basic \
448+
--scopes openid,email,offline_access \
449+
--no-pkce
450+
```
451+
452+
#### Signed and encrypted JWT
453+
454+
``` sh
455+
oauth2c https://oauth2c.us.authz.cloudentity.io/oauth2c/demo \
456+
--client-id cauosoo2omc4fr8ai1fg \
457+
--client-secret ipFkA1lMomOMI_d2HcGGQ7j8oxeHFqKw3kli76g92VM \
458+
--response-types code \
459+
--response-mode query.jwt \
460+
--grant-type authorization_code \
461+
--auth-method client_secret_post \
462+
--scopes openid,email,offline_access \
463+
--encryption-key https://raw.githubusercontent.com/cloudentity/oauth2c/master/data/key.json \
464+
--no-pkce
465+
```
466+
427467
## License
428468

429469
`oauth2c` is released under the [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0).

cmd/log.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import (
77
"crypto/x509"
88
"encoding/json"
99
"encoding/pem"
10+
"github.com/go-jose/go-jose/v3/jwt"
1011
"strconv"
1112
"strings"
1213

1314
"github.com/cloudentity/oauth2c/internal/oauth2"
14-
"github.com/golang-jwt/jwt"
1515
"github.com/grantae/certinfo"
1616
"github.com/pterm/pterm"
1717
"github.com/tidwall/pretty"
@@ -214,16 +214,17 @@ func LogRequestAndResponseln(request oauth2.Request, response interface{}) {
214214

215215
func LogTokenPayload(response oauth2.TokenResponse) {
216216
var (
217-
atClaims jwt.MapClaims
218-
idClaims jwt.MapClaims
217+
atClaims map[string]interface{}
218+
idClaims map[string]interface{}
219+
err error
219220
)
220221

221222
if silent {
222223
return
223224
}
224225

225226
if response.AccessToken != "" {
226-
if _, _, err := parser.ParseUnverified(response.AccessToken, &atClaims); err != nil {
227+
if _, atClaims, err = oauth2.UnsafeParseJWT(response.AccessToken); err != nil {
227228
pterm.Error.Println(err)
228229
} else {
229230
pterm.Println(pterm.FgGray.Sprint("Access token:"))
@@ -232,7 +233,7 @@ func LogTokenPayload(response oauth2.TokenResponse) {
232233
}
233234

234235
if response.IDToken != "" {
235-
if _, _, err := parser.ParseUnverified(response.IDToken, &idClaims); err != nil {
236+
if _, idClaims, err = oauth2.UnsafeParseJWT(response.IDToken); err != nil {
236237
pterm.Error.Println(err)
237238
} else {
238239
pterm.Println(pterm.FgGray.Sprint("ID token:"))
@@ -262,11 +263,22 @@ func LogAuthMethod(config oauth2.ClientConfig) {
262263
}
263264
}
264265

266+
func LogJARM(request oauth2.Request) {
267+
if silent {
268+
return
269+
}
270+
271+
if len(request.JARM) != 0 {
272+
pterm.Println(pterm.FgGray.Sprint("JARM:"))
273+
LogJson(request.JARM)
274+
}
275+
}
276+
265277
func LogAssertion(request oauth2.Request, title string, name string) {
266278
var (
267279
assertion = request.Form.Get(name)
268-
token *jwt.Token
269-
claims jwt.MapClaims
280+
token *jwt.JSONWebToken
281+
claims map[string]interface{}
270282
err error
271283
)
272284

@@ -278,12 +290,12 @@ func LogAssertion(request oauth2.Request, title string, name string) {
278290
return
279291
}
280292

281-
if token, _, err = parser.ParseUnverified(assertion, &claims); err != nil {
293+
if token, claims, err = oauth2.UnsafeParseJWT(assertion); err != nil {
282294
pterm.Error.Println(err)
283295
return
284296
}
285297

286-
pterm.DefaultBox.WithTitle(title).Printfln("%s = JWT-%s(payload)", name, token.Header["alg"])
298+
pterm.DefaultBox.WithTitle(title).Printfln("%s = JWT-%s(payload)", name, token.Headers[0].Algorithm)
287299
pterm.Println()
288300
pterm.Println("Payload")
289301
LogJson(claims)
@@ -330,16 +342,17 @@ func LogSubjectTokenAndActorToken(request oauth2.Request) {
330342
var (
331343
subjectToken = request.Form.Get("subject_token")
332344
actorToken = request.Form.Get("actor_token")
333-
subjectTokenClaims jwt.MapClaims
334-
actorTokenClaims jwt.MapClaims
345+
subjectTokenClaims map[string]interface{}
346+
actorTokenClaims map[string]interface{}
347+
err error
335348
)
336349

337350
if silent {
338351
return
339352
}
340353

341354
if subjectToken != "" {
342-
if _, _, err := parser.ParseUnverified(subjectToken, &subjectTokenClaims); err != nil {
355+
if _, subjectTokenClaims, err = oauth2.UnsafeParseJWT(subjectToken); err != nil {
343356
pterm.Error.Println(err)
344357
} else {
345358
pterm.Println(pterm.FgGray.Sprint("Subject token:"))
@@ -348,7 +361,7 @@ func LogSubjectTokenAndActorToken(request oauth2.Request) {
348361
}
349362

350363
if actorToken != "" {
351-
if _, _, err := parser.ParseUnverified(actorToken, &actorTokenClaims); err != nil {
364+
if _, actorTokenClaims, err = oauth2.UnsafeParseJWT(actorToken); err != nil {
352365
pterm.Error.Println(err)
353366
} else {
354367
pterm.Println(pterm.FgGray.Sprint("Actor token:"))

cmd/oauth2.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9-
"io/ioutil"
9+
"io"
1010
"net/http"
1111
"os"
1212
"strings"
1313
"time"
1414

1515
"github.com/cloudentity/oauth2c/internal/oauth2"
16-
"github.com/golang-jwt/jwt"
1716
"github.com/imdario/mergo"
1817
"github.com/pkg/browser"
1918
"github.com/spf13/cobra"
@@ -24,7 +23,6 @@ const (
2423
)
2524

2625
var (
27-
parser jwt.Parser
2826
silent bool
2927
)
3028

@@ -61,6 +59,7 @@ func NewOAuth2Cmd() (cmd *OAuth2Cmd) {
6159
cmd.PersistentFlags().BoolVar(&cconfig.NoPKCE, "no-pkce", false, "disable proof key for code exchange (PKCE)")
6260
cmd.PersistentFlags().StringVar(&cconfig.Assertion, "assertion", "", "claims for jwt bearer assertion")
6361
cmd.PersistentFlags().StringVar(&cconfig.SigningKey, "signing-key", "", "path or url to signing key in jwks format")
62+
cmd.PersistentFlags().StringVar(&cconfig.EncryptionKey, "encryption-key", "", "path or url to encryption key in jwks format")
6463
cmd.PersistentFlags().StringVar(&cconfig.SubjectToken, "subject-token", "", "third party access token")
6564
cmd.PersistentFlags().StringVar(&cconfig.SubjectTokenType, "subject-token-type", "", "third party access token type")
6665
cmd.PersistentFlags().StringVar(&cconfig.ActorToken, "actor-token", "", "acting party access token")
@@ -98,7 +97,7 @@ func (c *OAuth2Cmd) Run(cconfig *oauth2.ClientConfig) func(cmd *cobra.Command, a
9897
}
9998

10099
if silent {
101-
browser.Stdout = ioutil.Discard
100+
browser.Stdout = io.Discard
102101
}
103102

104103
tr := &http.Transport{

cmd/oauth2_authorize_code.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ func (c *OAuth2Cmd) AuthorizationCodeGrantFlow(clientConfig oauth2.ClientConfig,
4545
// callback
4646
callbackStatus := LogAction("Waiting for callback. Go to the browser to authenticate...")
4747

48-
if callbackRequest, err = oauth2.WaitForCallback(addr); err != nil {
48+
if callbackRequest, err = oauth2.WaitForCallback(clientConfig, serverConfig, addr, hc); err != nil {
4949
LogRequestln(callbackRequest)
5050
return err
5151
}
5252

5353
LogRequest(callbackRequest)
54+
LogJARM(callbackRequest)
5455
Logln()
5556

5657
callbackStatus("Obtained authorization code")

cmd/oauth2_implicit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (c *OAuth2Cmd) ImplicitGrantFlow(clientConfig oauth2.ClientConfig, serverCo
3636
// callback
3737
callbackStatus := LogAction("Waiting for callback. Go to the browser to authenticate...")
3838

39-
if callbackRequest, err = oauth2.WaitForCallback(addr); err != nil {
39+
if callbackRequest, err = oauth2.WaitForCallback(clientConfig, serverConfig, addr, hc); err != nil {
4040
LogRequestln(callbackRequest)
4141
return err
4242
}

data/key.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@
1313
"alg": "RS256",
1414
"dq": "rZYrOre0aanEH0tmvlQj860ctleHG0p_PScqMVhJmbMB8cQsahUul0c5KnZFntgk7Jmwc-EmMEJ1AzGncBiJhrpNd2u9M6N7DziUfleqEObblGB0bD4CdBREZkzHt1eBNKvMLgJX6nT-AeP9_cIaQcVwQlgtJK9DQWgqilFWHu0",
1515
"n": "rhipCrDSyEJpr8JJnBORLXb4jYbzCDNJAYCUCuYts-z7iLTnfNv2AkmphbY9EpGk1j96IQZq7g4fwFLh5HS9SFEPpTRh2-5Pp1QRnd-nhSaeT7hkVXGTGjlmRDHgv1-69_MZFSuBFA9I3yzdT7LlkWwPZS7WL5MYHNtbLJSIF1ls-MLleGci5qWCcLXPqMpeG_VEA53IhfIcVIMDU3g3gWqqEM7CTWdkdJ12fUyMpEPzF1VOYGadO181zdo6sIkdyWqAoCesUv_9Xpi9weJT_yduiInb0xzpsriP_U-dXwHf0ULI7vKIqa--WMbGUHD974tSxTOknYvRSyGRHWClmw"
16+
},
17+
{
18+
"p": "9vhNBMIdhLaHRat2mpMDy9AbSAb8ppCb5OZghpY14f97YN1CV4_DTnP7BZkAOiCvowdLrvTDbeu4uzG2ddND3OOIybvEFXb_Y7OAd_hWK9iAb6gN8n99nPqg-tggcD7V6ojgGgfO6EqJjdqXO-HkAcFZeZXs14cXtegCxYvOj3M",
19+
"kty": "RSA",
20+
"q": "riY7i5IZBVcvnZetVV-3Ymk0bOMYe-PR_CPMSVaXW7ZDtJ_1VWz_3LzOAGmcFprLkAFwlKNR16tDx6SudidGq0m3G7hac2-xvP5BW-vmM3jJagVidS98hlRLu3fYKzjsk6JYNkOUJZLjaHjaZD8vUqUC3rsJJyZIViNLn5gTBNE",
21+
"d": "P_po-RCxwXonz0zxzntTeKRVK9xVATXlj553whN-5bo0f_v3awbPzs6gJG-Je7bYZF_csOLYnB31oHz0uam4uAwfcJnDigQDLM-lPPgtTg7rr8cpPXDqK-ee1Gb55sh66Tv8qm0AmNUpikNV4hm4chmAf0kSbGBCUzljtAk4jJ_wEvuF3nDxvQBzb8LcOykiwJD7rSmk-PfBSaSTECYX4YMNWokUROD0ok9YBPY1yL82Lewzw45D8jwcbcBuFMXnNaweCZDhofF5O7mQU2LEYKKIuMWyFB8Y-DXF3KcnNWpFv_sDQTMr4zUnBLexefDIFyWJ68xjW6LclBwQA4DyIQ",
22+
"e": "AQAB",
23+
"use": "enc",
24+
"kid": "Gde0lSdovmeQDnL86WBa4n7eJyTfX61Vpzge2zp-dOg",
25+
"qi": "maaxlgAx7DPvsHrgyAjjJ_dN0L5hBhLHipIRSeufsEn8d120ySaIOj16yw2cZN30bXZx6oo2OuBU_V9_lv1UgB8cja4l41sG4joyGu247IQHeAFTGD0E5_vB13g37D4NnqIo4vmilSq-7FKwyVbH1Qicm_uHu8WjTkJytUXHJfg",
26+
"dp": "usftj-SzaHSXd-SGrb70RwecKmxGVj7V-FcXt5IRLFwJVqfgDdMHleT3ezNMzal8zCKvZaFt0EtPihu97_yYvY1EHbYpRUabPi86wQELQtvZvxhte_JZ8QhICY69ccbECD3-pxEytdHxmFwytrJPu9gcMG1oE1TzZLsUpYzn6us",
27+
"alg": "RSA-OAEP-256",
28+
"dq": "lRCSzYArvJ-JWWq4aqh4j1sgDchBb-JtHlcCCRB-lHkp8RUuaYXYaPiPmFjNy6eKcORamlBFwMaPSPdUgjxSSFHb03V0rvS_fddg2K7Op-ZO-VpMkaQpAc5r2LbXqP-buS8wNJqZ16Oo1gcEYOSYvDQUhdQxHHqvVlP16lDAjtE",
29+
"n": "qAGmpoooijsopTzj9cEEgLX9O3_e0nR-Y8QhSAgiUGkkpwpNhFYR96zcfRlTRi0IOpghvBw0DqIb8kgAJmOAyEdjT8k5wi7OvbFYSzk67r0bd4U_OVohQ4Df0m79pgYcRGL8OBscdpitt7FOC2xGaBgSD8BWjF9h_GgqZjI0-AqOSPHWIcD3z08QeuoLB0_QG2B4XXF8TCpIqAILiiKTHwz6pqHMnxomBgAZot49FqlRJEVVbfxsNwbWStEoolskNi_2lKKanUIuz5gz5G6CXzzrSGKTgJVmBvRS9hLDSzc5_SaE4JlWqDWaoa0KLeEzblj9zmehb346LChkS2no4w"
1630
}
1731
]
1832
}

data/public.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77
"kid":"Ana-TpIxraP9mCAwyAbxk40LqpE5Utfjjd4EQrc6sBM",
88
"alg":"RS256",
99
"n":"rhipCrDSyEJpr8JJnBORLXb4jYbzCDNJAYCUCuYts-z7iLTnfNv2AkmphbY9EpGk1j96IQZq7g4fwFLh5HS9SFEPpTRh2-5Pp1QRnd-nhSaeT7hkVXGTGjlmRDHgv1-69_MZFSuBFA9I3yzdT7LlkWwPZS7WL5MYHNtbLJSIF1ls-MLleGci5qWCcLXPqMpeG_VEA53IhfIcVIMDU3g3gWqqEM7CTWdkdJ12fUyMpEPzF1VOYGadO181zdo6sIkdyWqAoCesUv_9Xpi9weJT_yduiInb0xzpsriP_U-dXwHf0ULI7vKIqa--WMbGUHD974tSxTOknYvRSyGRHWClmw"
10+
},
11+
{
12+
"kty": "RSA",
13+
"e": "AQAB",
14+
"use": "enc",
15+
"kid": "Gde0lSdovmeQDnL86WBa4n7eJyTfX61Vpzge2zp-dOg",
16+
"alg": "RSA-OAEP-256",
17+
"n": "qAGmpoooijsopTzj9cEEgLX9O3_e0nR-Y8QhSAgiUGkkpwpNhFYR96zcfRlTRi0IOpghvBw0DqIb8kgAJmOAyEdjT8k5wi7OvbFYSzk67r0bd4U_OVohQ4Df0m79pgYcRGL8OBscdpitt7FOC2xGaBgSD8BWjF9h_GgqZjI0-AqOSPHWIcD3z08QeuoLB0_QG2B4XXF8TCpIqAILiiKTHwz6pqHMnxomBgAZot49FqlRJEVVbfxsNwbWStEoolskNi_2lKKanUIuz5gz5G6CXzzrSGKTgJVmBvRS9hLDSzc5_SaE4JlWqDWaoa0KLeEzblj9zmehb346LChkS2no4w"
1018
}
1119
]
1220
}

go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ require (
66
github.com/go-jose/go-jose/v3 v3.0.0
77
github.com/golang-jwt/jwt v3.2.2+incompatible
88
github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b
9+
github.com/hashicorp/go-multierror v1.1.1
910
github.com/imdario/mergo v0.3.13
11+
github.com/itchyny/gojq v0.12.10
1012
github.com/lithammer/shortuuid/v4 v4.0.0
1113
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
1214
github.com/pkg/errors v0.9.1
@@ -23,8 +25,8 @@ require (
2325
github.com/davecgh/go-spew v1.1.1 // indirect
2426
github.com/google/uuid v1.3.0 // indirect
2527
github.com/gookit/color v1.5.0 // indirect
28+
github.com/hashicorp/errwrap v1.0.0 // indirect
2629
github.com/inconshreveable/mousetrap v1.0.0 // indirect
27-
github.com/itchyny/gojq v0.12.10 // indirect
2830
github.com/itchyny/timefmt-go v0.1.5 // indirect
2931
github.com/lithammer/fuzzysearch v1.1.5 // indirect
3032
github.com/mattn/go-runewidth v0.0.14 // indirect

go.sum

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyM
2121
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
2222
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
2323
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
24-
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
2524
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
2625
github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
2726
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -31,6 +30,10 @@ github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
3130
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
3231
github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b h1:NGgE5ELokSf2tZ/bydyDUKrvd/jP8lrAoPNeBuMOTOk=
3332
github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b/go.mod h1:zT/uzhdQGTqlwTq7Lpbj3JoJQWfPfIJ1tE0OidAmih8=
33+
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
34+
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
35+
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
36+
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
3437
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
3538
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
3639
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -52,7 +55,6 @@ github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMr
5255
github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q=
5356
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
5457
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
55-
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
5658
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
5759
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
5860
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -105,7 +107,6 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
105107
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
106108
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
107109
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
108-
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
109110
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
110111
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
111112
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

0 commit comments

Comments
 (0)