From 5cf1bb7f4df93ef99478672c18b611c59919054b Mon Sep 17 00:00:00 2001 From: Harold Wanyama Date: Mon, 11 Sep 2023 17:57:18 +0300 Subject: [PATCH 1/2] Feature/Docusign support - Added golang feature for enabling individual and ccla sign process Signed-off-by: Harold Wanyama --- cla-backend-go/go.mod | 1 + cla-backend-go/go.sum | 2 + cla-backend-go/serverless.yml | 3 + cla-backend-go/swagger/cla.v2.yaml | 74 ++++++++++++++++++++ cla-backend-go/v2/docusign_auth/auth.go | 90 +++++++++++++++++++++++++ cla-backend-go/v2/sign/handlers.go | 24 +++++++ cla-backend-go/v2/sign/service.go | 30 +++++++++ 7 files changed, 224 insertions(+) create mode 100644 cla-backend-go/v2/docusign_auth/auth.go diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod index 2a213510b..ad18f3b97 100644 --- a/cla-backend-go/go.mod +++ b/cla-backend-go/go.mod @@ -74,6 +74,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230321155629-9a39f2531310 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/cloudflare/circl v1.3.2 // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/docker/go-units v0.4.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum index ad5c78d64..4557d1598 100644 --- a/cla-backend-go/go.sum +++ b/cla-backend-go/go.sum @@ -98,6 +98,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= diff --git a/cla-backend-go/serverless.yml b/cla-backend-go/serverless.yml index 8f96b02f9..0f5eb3f50 100644 --- a/cla-backend-go/serverless.yml +++ b/cla-backend-go/serverless.yml @@ -237,6 +237,9 @@ provider: DOCUSIGN_USERNAME: ${file(./env.json):docusign-username, ssm:/cla-docusign-username-${opt:stage}} DOCUSIGN_PASSWORD: ${file(./env.json):docusign-password, ssm:/cla-docusign-password-${opt:stage}} DOCUSIGN_INTEGRATOR_KEY: ${file(./env.json):docusign-integrator-key, ssm:/cla-docusign-integrator-key-${opt:stage}} + DOCUSIGN_AUTH_SERVER: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-auth-server-${opt:stage}} + DOCUSIGN_PRIVATE_KEY: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-private-key-${opt:stage}} + DOCUSIGN_USER_ID: ${file(./env.json):docusign-auth-server, ssm:/cla-docusign-user-id-${opt:stage}} CLA_API_BASE: ${file(./env.json):cla-api-base, ssm:/cla-api-base-${opt:stage}} CLA_CONTRIBUTOR_BASE: ${file(./env.json):cla-contributor-base, ssm:/cla-contributor-base-${opt:stage}} CLA_CONTRIBUTOR_V2_BASE: ${file(./env.json):cla-contributor-v2-base, ssm:/cla-contributor-v2-base-${opt:stage}} diff --git a/cla-backend-go/swagger/cla.v2.yaml b/cla-backend-go/swagger/cla.v2.yaml index 43397ebbb..64eb14c05 100644 --- a/cla-backend-go/swagger/cla.v2.yaml +++ b/cla-backend-go/swagger/cla.v2.yaml @@ -4125,6 +4125,36 @@ paths: tags: - gitlab-sign + + /request-individual-signature: + post: + summary: Request for icla sign + description: Initiate the icla signing with docusign + security: [] + operationId: requestIndividualSignature + parameters: + - $ref: "#/parameters/x-request-id" + - name: input + in: body + schema: + $ref: '#/definitions/icla-signature-input' + required: true + responses: + '200': + description: 'Success' + headers: + x-request-id: + type: string + description: The unique request ID value - assigned/set by the API Gateway based on the session + schema: + $ref: '#/definitions/individual-signature-output' + '400': + $ref: '#/responses/invalid-request' + '500': + $ref: '#/responses/internal-server-error' + tags: + - sign + responses: unauthorized: description: Unauthorized @@ -5497,6 +5527,40 @@ definitions: corporate-contributor: $ref: './common/corporate-contributor.yaml' + icla-signature-input: + type: object + required: + - project_sfid + - company_sfid + properties: + project_sfid: + type: string + example: 'a0941000005ouJFAAY' + description: salesforce id of the project + company_sfid: + type: string + example: '0014100000Te0fMAAR' + description: salesforce id of the company + send_as_email: + type: boolean + example: false + description: send signing request as email. This should be set to true when requestor is not signatory. + authority_name: + type: string + example: "Derk Miyamoto" + description: the name of the CLA signatory + minLength: 2 + maxLength: 255 + authority_email: + $ref: './common/properties/email.yaml' + description: the email of the CLA Signatory + return_url: + type: string + example: 'https://corporate.dev.lfcla.com/#/company/eb4d7d71-693f-4047-bf8d-10d0e7764969' + description: on signing the document, page will get redirected to this url. This is valid only when send_as_email is false + format: uri + + corporate-signature-input: type: object required: @@ -5544,6 +5608,16 @@ definitions: type: string description: signing url + individual-signature-output: + type: object + properties: + signature_id: + type: string + description: id of the signature + sign_url: + type: string + description: signing url + signed_document: type: object properties: diff --git a/cla-backend-go/v2/docusign_auth/auth.go b/cla-backend-go/v2/docusign_auth/auth.go new file mode 100644 index 000000000..96b137cb4 --- /dev/null +++ b/cla-backend-go/v2/docusign_auth/auth.go @@ -0,0 +1,90 @@ +// Copyright The Linux Foundation and each contributor to CommunityBridge. +// SPDX-License-Identifier: MIT + +package docusignauth + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "strings" + "time" + + jwt "github.com/dgrijalva/jwt-go" +) + +var ( + baseURL = os.Getenv("DOCUSIGN_BASE_URL") + oauthTokenURL = baseURL + "/oauth/token" + jwtGrantAssertion = "urn:ietf:params:oauth:grant-type:jwt-bearer" +) + +type TokenResponse struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` +} + +func GetAccessToken(integrationKey, userGUID, privateKey string) (string, error) { + // Generate the JWT token + tokenString, err := generateJWT(integrationKey, userGUID, privateKey) + if err != nil { + return "", err + } + + // Make the HTTP request to get the access token + body := strings.NewReader(fmt.Sprintf("grant_type=%s&assertion=%s", jwtGrantAssertion, tokenString)) + req, err := http.NewRequest("POST", oauthTokenURL, body) + if err != nil { + return "", err + } + + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() // nolint + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("failed to get access token, status: %s", resp.Status) + } + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + var tokenResponse TokenResponse + if err := json.Unmarshal(respBody, &tokenResponse); err != nil { + return "", err + } + + return tokenResponse.AccessToken, nil +} + +func generateJWT(integrationKey, userGUID, privateKey string) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{ + "iss": integrationKey, // Integration Key + "sub": userGUID, // User GUID + "aud": baseURL, // Base URL + "iat": time.Now().Unix(), // Issued At + "exp": time.Now().Add(1 * time.Hour).Unix(), // Expiration time - 1 hour is recommended + "scope": "signature", // Permission scope + }) + + signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey)) + if err != nil { + return "", err + } + + tokenString, err := token.SignedString(signKey) + if err != nil { + return "", err + } + + return tokenString, nil +} diff --git a/cla-backend-go/v2/sign/handlers.go b/cla-backend-go/v2/sign/handlers.go index 4d578f994..d3c743acc 100644 --- a/cla-backend-go/v2/sign/handlers.go +++ b/cla-backend-go/v2/sign/handlers.go @@ -4,6 +4,7 @@ package sign import ( + "context" "errors" "fmt" "strings" @@ -20,6 +21,7 @@ import ( "github.com/communitybridge/easycla/cla-backend-go/utils" "github.com/communitybridge/easycla/cla-backend-go/v2/organization-service/client/organizations" "github.com/go-openapi/runtime/middleware" + ) // Configure API call @@ -75,6 +77,28 @@ func Configure(api *operations.EasyclaAPI, service Service) { } return sign.NewRequestCorporateSignatureOK().WithPayload(resp) }) + + api.SignRequestIndividualSignatureHandler = sign.RequestIndividualSignatureHandlerFunc( + func(params sign.RequestIndividualSignatureParams) middleware.Responder { + reqId := utils.GetRequestID(params.XREQUESTID) + ctx := context.WithValue(params.HTTPRequest.Context(), utils.XREQUESTID, reqId) + f := logrus.Fields{ + "functionName": "v2.sign.handlers.SignRequestIndividualSignatureHandler", + utils.XREQUESTID: ctx.Value(utils.XREQUESTID), + "CompanyID": params.Input.CompanySfid, + "ProjectSFID": params.Input.ProjectSfid, + "authorityName": params.Input.AuthorityName, + "authorityEmail": params.Input.AuthorityEmail, + } + log.WithFields(f).Debug("processing request") + resp, err := service.RequestIndividualSignature(ctx, params.Input) + if err != nil { + log.WithFields(f).WithError(err).Warn("problem requesting individual signature") + return sign.NewRequestIndividualSignatureBadRequest().WithPayload(errorResponse(reqId, err)) + } + return sign.NewRequestIndividualSignatureOK().WithPayload(resp) + }) + } type codedResponse interface { diff --git a/cla-backend-go/v2/sign/service.go b/cla-backend-go/v2/sign/service.go index 907eec7e7..99b282687 100644 --- a/cla-backend-go/v2/sign/service.go +++ b/cla-backend-go/v2/sign/service.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "net/http" + "os" "strings" "github.com/communitybridge/easycla/cla-backend-go/projects_cla_groups" @@ -32,6 +33,13 @@ import ( v1Models "github.com/communitybridge/easycla/cla-backend-go/gen/v1/models" "github.com/communitybridge/easycla/cla-backend-go/gen/v2/models" "github.com/communitybridge/easycla/cla-backend-go/utils" + "github.com/communitybridge/easycla/cla-backend-go/v2/docusign_auth" +) + +var ( + integrationKey = os.Getenv("DOCUSIGN_INTEGRATOR_KEY") + userGUID = os.Getenv("DOCUSIGN_USER_ID") + privateKey = os.Getenv("DOCUSIGN_PRIVATE_KEY") ) // constants @@ -54,6 +62,7 @@ type ProjectRepo interface { // Service interface defines the sign service methods type Service interface { RequestCorporateSignature(ctx context.Context, lfUsername string, authorizationHeader string, input *models.CorporateSignatureInput) (*models.CorporateSignatureOutput, error) + RequestIndividualSignature(ctx context.Context, input *models.IclaSignatureInput) (*models.IndividualSignatureOutput, error) } // service @@ -302,6 +311,27 @@ func (s *service) RequestCorporateSignature(ctx context.Context, lfUsername stri return out.toModel(), nil } +func (s *service) RequestIndividualSignature(ctx context.Context, input *models.IclaSignatureInput) (*models.IndividualSignatureOutput, error) { + f := logrus.Fields{ + "functionName": "sign.RequestIndividualSignature", + "authorityEmail": input.AuthorityEmail, + "authorityName": input.AuthorityName, + "companySFID": input.CompanySfid, + "projectSFID": input.ProjectSfid, + } + + log.WithFields(f).Debug("Get Access Token for DocuSign") + accessToken, err := docusignauth.GetAccessToken(integrationKey, userGUID, privateKey) + if err != nil { + log.WithFields(f).WithError(err).Warn("unable to get access token for DocuSign") + return nil, err + } + + log.WithFields(f).Debugf("access token: %s", accessToken) + return nil, nil + +} + func requestCorporateSignature(authToken string, apiURL string, input *requestCorporateSignatureInput) (*requestCorporateSignatureOutput, error) { f := logrus.Fields{ "functionName": "requestCorporateSignature", From 20efa079e92cd44975497873ab45b30bceb98594 Mon Sep 17 00:00:00 2001 From: Harold Wanyama Date: Mon, 18 Sep 2023 15:42:17 +0300 Subject: [PATCH 2/2] Golang Docusign support Signed-off-by: Harold Wanyama --- cla-backend-go/go.mod | 1 + cla-backend-go/go.sum | 2 ++ cla-backend-go/v2/docusign_auth/auth.go | 16 +++++++-- cla-backend-go/v2/main/main.go | 47 +++++++++++++++++++++++++ cla-backend-go/v2/sign/docusign.go | 18 ++++++++++ cla-backend-go/v2/sign/jwt.go | 17 +++++++++ 6 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 cla-backend-go/v2/main/main.go create mode 100644 cla-backend-go/v2/sign/docusign.go create mode 100644 cla-backend-go/v2/sign/jwt.go diff --git a/cla-backend-go/go.mod b/cla-backend-go/go.mod index ad18f3b97..2105701bc 100644 --- a/cla-backend-go/go.mod +++ b/cla-backend-go/go.mod @@ -84,6 +84,7 @@ require ( github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-github/v50 v50.2.0 // indirect github.com/google/go-querystring v1.1.0 // indirect diff --git a/cla-backend-go/go.sum b/cla-backend-go/go.sum index 4557d1598..45ec94f36 100644 --- a/cla-backend-go/go.sum +++ b/cla-backend-go/go.sum @@ -271,6 +271,8 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/dep v0.5.4/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA= diff --git a/cla-backend-go/v2/docusign_auth/auth.go b/cla-backend-go/v2/docusign_auth/auth.go index 96b137cb4..72f1e6bd4 100644 --- a/cla-backend-go/v2/docusign_auth/auth.go +++ b/cla-backend-go/v2/docusign_auth/auth.go @@ -4,7 +4,9 @@ package docusignauth import ( + "crypto/x509" "encoding/json" + "encoding/pem" "fmt" "io" "net/http" @@ -16,7 +18,7 @@ import ( ) var ( - baseURL = os.Getenv("DOCUSIGN_BASE_URL") + baseURL = os.Getenv("DOCUSIGN_AUTH_SERVER") oauthTokenURL = baseURL + "/oauth/token" jwtGrantAssertion = "urn:ietf:params:oauth:grant-type:jwt-bearer" ) @@ -27,8 +29,18 @@ type TokenResponse struct { } func GetAccessToken(integrationKey, userGUID, privateKey string) (string, error) { + block, _ := pem.Decode([]byte(privateKey)) + if block == nil { + return "", fmt.Errorf("failed to parse private key") + } + + privateKeyParsed, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return "", err + } + // Generate the JWT token - tokenString, err := generateJWT(integrationKey, userGUID, privateKey) + tokenString, err := generateJWT(integrationKey, userGUID, privateKeyParsed.D.String()) if err != nil { return "", err } diff --git a/cla-backend-go/v2/main/main.go b/cla-backend-go/v2/main/main.go new file mode 100644 index 000000000..d220a74c4 --- /dev/null +++ b/cla-backend-go/v2/main/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + + "github.com/communitybridge/easycla/cla-backend-go/v2/docusign_auth" +) + +func main() { + integrationKey := "557677f2-1e2f-4955-aaa6-1ef44630e01d" + userID := "3a1d118f-3083-4c25-8306-11b7400f7c03" + privateKey :=` + -----BEGIN RSA PRIVATE KEY----- + MIIEogIBAAKCAQEAh0M2mIGaJjP8S/FxZR7nRsatCR/KpCPBFBbxalZffykqtTID + KNeDhJ5RvJKAVoJlLaLoUYSYloVaeSAwQdbn4F+Lsnll3mCGocwdl/W8998Lc/Ln + MaNQhpekBoXaq8vbj251jxnRcsdI9yl/YyQo3jZnM77OWtEF7dyvS6V9cMprT2Ca + eIJPj0Ck3/P9rGwE3DdiEXZDetgkuNQyMavfvLKltCNu3qQXFA95PXlHs2E57OrS + CNIQT37jfInXuIyCoGjDSq+U3ZE047UG5Id8/OFvcP1z5iUdwvueoEximt/kSvR0 + ZcNfjqyJnnYq80OKwTdalZEOzAEkt/U2QuB9jwIDAQABAoIBAB01GstopOgd7ptZ + efJrb2ZdjUy8lC3IWK9lWuDq4LkdIw84Su1dSBVxeFXfTp4fjwiBNmgv2SEbj5M7 + K6Bz7uMIzqoNw7z2m+vBHxzKn/DoNVlmuJyD1uYRRYZxDext2y3IHNN3MD54IN3a + FJtMWhTNq5BFYdrDauPXdPTBOeqKQgMoQl6OwHIx4WJXYxRgIgrvBYRzLSwG/u4V + tAvj1/J0PSSOY5A1qn6L2ii9abVElFr2zZC5MtG3oG1TLCidzxZFt/5qSHGo+0tU + l/7YRXWxadfYrPmS9akbC7SQn9WJQdIlfejtuQ6hmKTCCjhTp2BUba3kTI3PcM46 + GzW3WQECgYEA08lwWFZng3bm3KrDx3ljjZ+cuL8vOXfLeYuaIPmNu5eXrR5ZR9Ar + UyUmnZbL8rhK9dNCmRGX293uuppwJolfMAy+kcvQ4HsRQ5tOgXmP8VhKzA4RRc+h + I7uIGQ7NmKI6Wm/wJ+T3Okw0h8ze8MW66/D5IHDQSoGQZH9kkA9+MQECgYEAo4AS + jB/b/SWhwrRUI8m+5Tjy5WO9ZkRZ54zpRnAr5HsjSBgb72IGbJKwleKwb3FxwPTQ + ZlgKP2O5rGzZOEZcIryIliCpV4C/XeFVwUKxo/A5jkL+U9ZQwoA0/0ssdE/7uLzG + FiKcFK7OhsFg+dNEX1ForM9cGN5OzY8sGzylHo8CgYAc0Nq1WkRJUeNFgQKUYILY + ITB8vp6ZTiBkUEdPV0Uekhi0GF4DdGKAtJxVctAbHVItsmnsU8V6x+6UezDpPWWz + Lvi686Ve9b+6mCYNXdHk/6NlskBNZFvDdd+lsSruKpyP840UkIXG69l15L0su2qc + cbQj4tWkXY6c7exr4X/FAQKBgDmGTABFDU9puBoa/CeDSci4Wq1ehDrA/ai8KS8B + NFA1CtrIsLtuj7gPfFWf5levYEh1WgVIIILhAWiq+1oTV0NZdezsHOiOgcX0DAns + /zcgw/9LjtPMaamlFgBkYIWjxnre4ArVrniQcFV1IDuFm16189ApPMv7G1qzbt8+ + XRH9AoGAceSrZLbceDkqgprTTV1BEOFY+Ti9edBIUW+CqN6KkB66OXHGWlab/PZc + OCHRPAjEijZcVXm8IPC2yi/s0agQAB8dKO2L1X0EtvxkWSg2s/YXFpp0QccQToTo + lRFb9injNzixUOq18Z62XcC/yqMB7QtgRw5x5OQk0HNWpL7h6KM= + -----END RSA PRIVATE KEY----- + ` + token, err := docusignauth.GetAccessToken(integrationKey, userID, privateKey) + if err != nil { + fmt.Println(err) + } + + fmt.Println(token) +} \ No newline at end of file diff --git a/cla-backend-go/v2/sign/docusign.go b/cla-backend-go/v2/sign/docusign.go new file mode 100644 index 000000000..394a332a6 --- /dev/null +++ b/cla-backend-go/v2/sign/docusign.go @@ -0,0 +1,18 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +package sign + +import ( + "context" + "log" +) + +func (s *service) getAccessToken(ctx context.Context) (string, error) { + f := logrus.Fields{ + "functionName": "sign.getAccessToken", + } + + // Get the access token + jwtAssertion, jwterr := jwtToken() +} \ No newline at end of file diff --git a/cla-backend-go/v2/sign/jwt.go b/cla-backend-go/v2/sign/jwt.go new file mode 100644 index 000000000..ac954d3e8 --- /dev/null +++ b/cla-backend-go/v2/sign/jwt.go @@ -0,0 +1,17 @@ +// Copyright The Linux Foundation and each contributor to LFX. +// SPDX-License-Identifier: MIT + +package sign + +import ( + "github.com/golang-jwt/jwt" + "github.com/sirupsen/logrus" +) + +const + +func jwtToken() (string, error) { + claims := jwt.MapClaims{ + "iss": , + } +} \ No newline at end of file