vault.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package vault
  13. import (
  14. "context"
  15. "crypto/tls"
  16. "crypto/x509"
  17. "errors"
  18. "fmt"
  19. "io/ioutil"
  20. "net/http"
  21. "os"
  22. "strings"
  23. "github.com/go-logr/logr"
  24. vault "github.com/hashicorp/vault/api"
  25. corev1 "k8s.io/api/core/v1"
  26. "k8s.io/apimachinery/pkg/types"
  27. ctrl "sigs.k8s.io/controller-runtime"
  28. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  29. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  30. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  31. "github.com/external-secrets/external-secrets/pkg/provider"
  32. "github.com/external-secrets/external-secrets/pkg/provider/schema"
  33. )
  34. var (
  35. _ provider.Provider = &connector{}
  36. _ provider.SecretsClient = &client{}
  37. )
  38. const (
  39. serviceAccTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
  40. errVaultStore = "received invalid Vault SecretStore resource: %w"
  41. errVaultClient = "cannot setup new vault client: %w"
  42. errVaultCert = "cannot set Vault CA certificate: %w"
  43. errReadSecret = "cannot read secret data from Vault: %w"
  44. errAuthFormat = "cannot initialize Vault client: no valid auth method specified: %w"
  45. errDataField = "failed to find data field"
  46. errJSONUnmarshall = "failed to unmarshall JSON"
  47. errSecretFormat = "secret data not in expected format"
  48. errVaultToken = "cannot parse Vault authentication token: %w"
  49. errVaultReqParams = "cannot set Vault request parameters: %w"
  50. errVaultRequest = "error from Vault request: %w"
  51. errVaultResponse = "cannot parse Vault response: %w"
  52. errServiceAccount = "cannot read Kubernetes service account token from file system: %w"
  53. errGetKubeSA = "cannot get Kubernetes service account %q: %w"
  54. errGetKubeSASecrets = "cannot find secrets bound to service account: %q"
  55. errGetKubeSANoToken = "cannot find token in secrets bound to service account: %q"
  56. errGetKubeSecret = "cannot get Kubernetes secret %q: %w"
  57. errSecretKeyFmt = "cannot find secret data for key: %q"
  58. errClientTLSAuth = "error from Client TLS Auth: %q"
  59. )
  60. type Client interface {
  61. NewRequest(method, requestPath string) *vault.Request
  62. RawRequestWithContext(ctx context.Context, r *vault.Request) (*vault.Response, error)
  63. SetToken(v string)
  64. SetNamespace(namespace string)
  65. }
  66. type client struct {
  67. kube kclient.Client
  68. store *esv1alpha1.VaultProvider
  69. log logr.Logger
  70. client Client
  71. namespace string
  72. storeKind string
  73. }
  74. func init() {
  75. schema.Register(&connector{
  76. newVaultClient: newVaultClient,
  77. }, &esv1alpha1.SecretStoreProvider{
  78. Vault: &esv1alpha1.VaultProvider{},
  79. })
  80. }
  81. func newVaultClient(c *vault.Config) (Client, error) {
  82. return vault.NewClient(c)
  83. }
  84. type connector struct {
  85. newVaultClient func(c *vault.Config) (Client, error)
  86. }
  87. func (c *connector) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
  88. storeSpec := store.GetSpec()
  89. if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Vault == nil {
  90. return nil, errors.New(errVaultStore)
  91. }
  92. vaultSpec := storeSpec.Provider.Vault
  93. vStore := &client{
  94. kube: kube,
  95. store: vaultSpec,
  96. log: ctrl.Log.WithName("provider").WithName("vault"),
  97. namespace: namespace,
  98. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  99. }
  100. cfg, err := vStore.newConfig()
  101. if err != nil {
  102. return nil, err
  103. }
  104. client, err := c.newVaultClient(cfg)
  105. if err != nil {
  106. return nil, fmt.Errorf(errVaultClient, err)
  107. }
  108. if vaultSpec.Namespace != nil {
  109. client.SetNamespace(*vaultSpec.Namespace)
  110. }
  111. if err := vStore.setAuth(ctx, client, cfg); err != nil {
  112. return nil, err
  113. }
  114. vStore.client = client
  115. return vStore, nil
  116. }
  117. func (v *client) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
  118. data, err := v.readSecret(ctx, ref.Key, ref.Version)
  119. if err != nil {
  120. return nil, err
  121. }
  122. value, exists := data[ref.Property]
  123. if !exists {
  124. return nil, fmt.Errorf(errSecretKeyFmt, ref.Property)
  125. }
  126. return value, nil
  127. }
  128. func (v *client) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  129. return v.readSecret(ctx, ref.Key, ref.Version)
  130. }
  131. func (v *client) Close(ctx context.Context) error {
  132. return nil
  133. }
  134. func (v *client) readSecret(ctx context.Context, path, version string) (map[string][]byte, error) {
  135. kvPath := v.store.Path
  136. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  137. if !strings.HasSuffix(kvPath, "/data") {
  138. kvPath = fmt.Sprintf("%s/data", kvPath)
  139. }
  140. }
  141. // path formated according to vault docs for v1 and v2 API
  142. // v1: https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret
  143. // v2: https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  144. req := v.client.NewRequest(http.MethodGet, fmt.Sprintf("/v1/%s/%s", kvPath, path))
  145. if version != "" {
  146. req.Params.Set("version", version)
  147. }
  148. resp, err := v.client.RawRequestWithContext(ctx, req)
  149. if err != nil {
  150. return nil, fmt.Errorf(errReadSecret, err)
  151. }
  152. vaultSecret, err := vault.ParseSecret(resp.Body)
  153. if err != nil {
  154. return nil, err
  155. }
  156. secretData := vaultSecret.Data
  157. if v.store.Version == esv1alpha1.VaultKVStoreV2 {
  158. // Vault KV2 has data embedded within sub-field
  159. // reference - https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
  160. dataInt, ok := vaultSecret.Data["data"]
  161. if !ok {
  162. return nil, errors.New(errDataField)
  163. }
  164. secretData, ok = dataInt.(map[string]interface{})
  165. if !ok {
  166. return nil, errors.New(errJSONUnmarshall)
  167. }
  168. }
  169. byteMap := make(map[string][]byte, len(secretData))
  170. for k, v := range secretData {
  171. switch t := v.(type) {
  172. case string:
  173. byteMap[k] = []byte(t)
  174. case []byte:
  175. byteMap[k] = t
  176. default:
  177. return nil, errors.New(errSecretFormat)
  178. }
  179. }
  180. return byteMap, nil
  181. }
  182. func (v *client) newConfig() (*vault.Config, error) {
  183. cfg := vault.DefaultConfig()
  184. cfg.Address = v.store.Server
  185. if len(v.store.CABundle) == 0 {
  186. return cfg, nil
  187. }
  188. caCertPool := x509.NewCertPool()
  189. ok := caCertPool.AppendCertsFromPEM(v.store.CABundle)
  190. if !ok {
  191. return nil, errors.New(errVaultCert)
  192. }
  193. if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
  194. transport.TLSClientConfig.RootCAs = caCertPool
  195. }
  196. return cfg, nil
  197. }
  198. func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error {
  199. tokenExists, err := setSecretKeyToken(ctx, v, client)
  200. if tokenExists {
  201. return err
  202. }
  203. tokenExists, err = setAppRoleToken(ctx, v, client)
  204. if tokenExists {
  205. return err
  206. }
  207. tokenExists, err = setKubernetesAuthToken(ctx, v, client)
  208. if tokenExists {
  209. return err
  210. }
  211. tokenExists, err = setLdapAuthToken(ctx, v, client)
  212. if tokenExists {
  213. return err
  214. }
  215. tokenExists, err = setJwtAuthToken(ctx, v, client)
  216. if tokenExists {
  217. return err
  218. }
  219. tokenExists, err = setCertAuthToken(ctx, v, client, cfg)
  220. if tokenExists {
  221. return err
  222. }
  223. return errors.New(errAuthFormat)
  224. }
  225. func setAppRoleToken(ctx context.Context, v *client, client Client) (bool, error) {
  226. tokenRef := v.store.Auth.TokenSecretRef
  227. if tokenRef != nil {
  228. token, err := v.secretKeyRef(ctx, tokenRef)
  229. if err != nil {
  230. return true, err
  231. }
  232. client.SetToken(token)
  233. return true, nil
  234. }
  235. return false, nil
  236. }
  237. func setSecretKeyToken(ctx context.Context, v *client, client Client) (bool, error) {
  238. appRole := v.store.Auth.AppRole
  239. if appRole != nil {
  240. token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole)
  241. if err != nil {
  242. return true, err
  243. }
  244. client.SetToken(token)
  245. return true, nil
  246. }
  247. return false, nil
  248. }
  249. func setKubernetesAuthToken(ctx context.Context, v *client, client Client) (bool, error) {
  250. kubernetesAuth := v.store.Auth.Kubernetes
  251. if kubernetesAuth != nil {
  252. token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth)
  253. if err != nil {
  254. return true, err
  255. }
  256. client.SetToken(token)
  257. return true, nil
  258. }
  259. return false, nil
  260. }
  261. func setLdapAuthToken(ctx context.Context, v *client, client Client) (bool, error) {
  262. ldapAuth := v.store.Auth.Ldap
  263. if ldapAuth != nil {
  264. token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
  265. if err != nil {
  266. return true, err
  267. }
  268. client.SetToken(token)
  269. return true, nil
  270. }
  271. return false, nil
  272. }
  273. func setJwtAuthToken(ctx context.Context, v *client, client Client) (bool, error) {
  274. jwtAuth := v.store.Auth.Jwt
  275. if jwtAuth != nil {
  276. token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
  277. if err != nil {
  278. return true, err
  279. }
  280. client.SetToken(token)
  281. return true, nil
  282. }
  283. return false, nil
  284. }
  285. func setCertAuthToken(ctx context.Context, v *client, client Client, cfg *vault.Config) (bool, error) {
  286. certAuth := v.store.Auth.Cert
  287. if certAuth != nil {
  288. token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg)
  289. if err != nil {
  290. return true, err
  291. }
  292. client.SetToken(token)
  293. return true, nil
  294. }
  295. return false, nil
  296. }
  297. func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
  298. serviceAccount := &corev1.ServiceAccount{}
  299. ref := types.NamespacedName{
  300. Namespace: v.namespace,
  301. Name: serviceAccountRef.Name,
  302. }
  303. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  304. (serviceAccountRef.Namespace != nil) {
  305. ref.Namespace = *serviceAccountRef.Namespace
  306. }
  307. err := v.kube.Get(ctx, ref, serviceAccount)
  308. if err != nil {
  309. return "", fmt.Errorf(errGetKubeSA, ref.Name, err)
  310. }
  311. if len(serviceAccount.Secrets) == 0 {
  312. return "", fmt.Errorf(errGetKubeSASecrets, ref.Name)
  313. }
  314. for _, tokenRef := range serviceAccount.Secrets {
  315. retval, err := v.secretKeyRef(ctx, &esmeta.SecretKeySelector{
  316. Name: tokenRef.Name,
  317. Namespace: &ref.Namespace,
  318. Key: "token",
  319. })
  320. if err != nil {
  321. continue
  322. }
  323. return retval, nil
  324. }
  325. return "", fmt.Errorf(errGetKubeSANoToken, ref.Name)
  326. }
  327. func (v *client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) {
  328. secret := &corev1.Secret{}
  329. ref := types.NamespacedName{
  330. Namespace: v.namespace,
  331. Name: secretRef.Name,
  332. }
  333. if (v.storeKind == esv1alpha1.ClusterSecretStoreKind) &&
  334. (secretRef.Namespace != nil) {
  335. ref.Namespace = *secretRef.Namespace
  336. }
  337. err := v.kube.Get(ctx, ref, secret)
  338. if err != nil {
  339. return "", fmt.Errorf(errGetKubeSecret, ref.Name, err)
  340. }
  341. keyBytes, ok := secret.Data[secretRef.Key]
  342. if !ok {
  343. return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key)
  344. }
  345. value := string(keyBytes)
  346. valueStr := strings.TrimSpace(value)
  347. return valueStr, nil
  348. }
  349. // appRoleParameters creates the required body for Vault AppRole Auth.
  350. // Reference - https://www.vaultproject.io/api-docs/auth/approle#login-with-approle
  351. func appRoleParameters(role, secret string) map[string]string {
  352. return map[string]string{
  353. "role_id": role,
  354. "secret_id": secret,
  355. }
  356. }
  357. func (v *client) requestTokenWithAppRoleRef(ctx context.Context, client Client, appRole *esv1alpha1.VaultAppRole) (string, error) {
  358. roleID := strings.TrimSpace(appRole.RoleID)
  359. secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef)
  360. if err != nil {
  361. return "", err
  362. }
  363. parameters := appRoleParameters(roleID, secretID)
  364. url := strings.Join([]string{"/v1", "auth", appRole.Path, "login"}, "/")
  365. request := client.NewRequest("POST", url)
  366. err = request.SetJSONBody(parameters)
  367. if err != nil {
  368. return "", fmt.Errorf(errVaultReqParams, err)
  369. }
  370. resp, err := client.RawRequestWithContext(ctx, request)
  371. if err != nil {
  372. return "", fmt.Errorf(errVaultRequest, err)
  373. }
  374. defer resp.Body.Close()
  375. vaultResult := vault.Secret{}
  376. if err = resp.DecodeJSON(&vaultResult); err != nil {
  377. return "", fmt.Errorf(errVaultResponse, err)
  378. }
  379. token, err := vaultResult.TokenID()
  380. if err != nil {
  381. return "", fmt.Errorf(errVaultToken, err)
  382. }
  383. return token, nil
  384. }
  385. // kubeParameters creates the required body for Vault Kubernetes auth.
  386. // Reference - https://www.vaultproject.io/api/auth/kubernetes#login
  387. func kubeParameters(role, jwt string) map[string]string {
  388. return map[string]string{
  389. "role": role,
  390. "jwt": jwt,
  391. }
  392. }
  393. func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
  394. jwtString, err := getJwtString(ctx, v, kubernetesAuth)
  395. if err != nil {
  396. return "", err
  397. }
  398. parameters := kubeParameters(kubernetesAuth.Role, jwtString)
  399. url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
  400. request := client.NewRequest("POST", url)
  401. err = request.SetJSONBody(parameters)
  402. if err != nil {
  403. return "", fmt.Errorf(errVaultReqParams, err)
  404. }
  405. resp, err := client.RawRequestWithContext(ctx, request)
  406. if err != nil {
  407. return "", fmt.Errorf(errVaultRequest, err)
  408. }
  409. defer resp.Body.Close()
  410. vaultResult := vault.Secret{}
  411. err = resp.DecodeJSON(&vaultResult)
  412. if err != nil {
  413. return "", fmt.Errorf(errVaultResponse, err)
  414. }
  415. token, err := vaultResult.TokenID()
  416. if err != nil {
  417. return "", fmt.Errorf(errVaultToken, err)
  418. }
  419. return token, nil
  420. }
  421. func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1alpha1.VaultKubernetesAuth) (string, error) {
  422. if kubernetesAuth.ServiceAccountRef != nil {
  423. jwt, err := v.secretKeyRefForServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
  424. if err != nil {
  425. return "", err
  426. }
  427. return jwt, nil
  428. } else if kubernetesAuth.SecretRef != nil {
  429. tokenRef := kubernetesAuth.SecretRef
  430. if tokenRef.Key == "" {
  431. tokenRef = kubernetesAuth.SecretRef.DeepCopy()
  432. tokenRef.Key = "token"
  433. }
  434. jwt, err := v.secretKeyRef(ctx, tokenRef)
  435. if err != nil {
  436. return "", err
  437. }
  438. return jwt, nil
  439. } else {
  440. // Kubernetes authentication is specified, but without a referenced
  441. // Kubernetes secret. We check if the file path for in-cluster service account
  442. // exists and attempt to use the token for Vault Kubernetes auth.
  443. if _, err := os.Stat(serviceAccTokenPath); err != nil {
  444. return "", fmt.Errorf(errServiceAccount, err)
  445. }
  446. jwtByte, err := ioutil.ReadFile(serviceAccTokenPath)
  447. if err != nil {
  448. return "", fmt.Errorf(errServiceAccount, err)
  449. }
  450. return string(jwtByte), nil
  451. }
  452. }
  453. func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1alpha1.VaultLdapAuth) (string, error) {
  454. username := strings.TrimSpace(ldapAuth.Username)
  455. password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef)
  456. if err != nil {
  457. return "", err
  458. }
  459. parameters := map[string]string{
  460. "password": password,
  461. }
  462. url := strings.Join([]string{"/v1", "auth", "ldap", "login", username}, "/")
  463. request := client.NewRequest("POST", url)
  464. err = request.SetJSONBody(parameters)
  465. if err != nil {
  466. return "", fmt.Errorf(errVaultReqParams, err)
  467. }
  468. resp, err := client.RawRequestWithContext(ctx, request)
  469. if err != nil {
  470. return "", fmt.Errorf(errVaultRequest, err)
  471. }
  472. defer resp.Body.Close()
  473. vaultResult := vault.Secret{}
  474. if err = resp.DecodeJSON(&vaultResult); err != nil {
  475. return "", fmt.Errorf(errVaultResponse, err)
  476. }
  477. token, err := vaultResult.TokenID()
  478. if err != nil {
  479. return "", fmt.Errorf(errVaultToken, err)
  480. }
  481. return token, nil
  482. }
  483. func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1alpha1.VaultJwtAuth) (string, error) {
  484. role := strings.TrimSpace(jwtAuth.Role)
  485. jwt, err := v.secretKeyRef(ctx, &jwtAuth.SecretRef)
  486. if err != nil {
  487. return "", err
  488. }
  489. parameters := map[string]string{
  490. "role": role,
  491. "jwt": jwt,
  492. }
  493. url := strings.Join([]string{"/v1", "auth", "jwt", "login"}, "/")
  494. request := client.NewRequest("POST", url)
  495. err = request.SetJSONBody(parameters)
  496. if err != nil {
  497. return "", fmt.Errorf(errVaultReqParams, err)
  498. }
  499. resp, err := client.RawRequestWithContext(ctx, request)
  500. if err != nil {
  501. return "", fmt.Errorf(errVaultRequest, err)
  502. }
  503. defer resp.Body.Close()
  504. vaultResult := vault.Secret{}
  505. if err = resp.DecodeJSON(&vaultResult); err != nil {
  506. return "", fmt.Errorf(errVaultResponse, err)
  507. }
  508. token, err := vaultResult.TokenID()
  509. if err != nil {
  510. return "", fmt.Errorf(errVaultToken, err)
  511. }
  512. return token, nil
  513. }
  514. func (v *client) requestTokenWithCertAuth(ctx context.Context, client Client, certAuth *esv1alpha1.VaultCertAuth, cfg *vault.Config) (string, error) {
  515. clientKey, err := v.secretKeyRef(ctx, &certAuth.SecretRef)
  516. if err != nil {
  517. return "", err
  518. }
  519. clientCert, err := v.secretKeyRef(ctx, &certAuth.ClientCert)
  520. if err != nil {
  521. return "", err
  522. }
  523. cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
  524. if err != nil {
  525. return "", fmt.Errorf(errClientTLSAuth, err)
  526. }
  527. if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
  528. transport.TLSClientConfig.Certificates = []tls.Certificate{cert}
  529. }
  530. url := strings.Join([]string{"/v1", "auth", "cert", "login"}, "/")
  531. request := client.NewRequest("POST", url)
  532. resp, err := client.RawRequestWithContext(ctx, request)
  533. if err != nil {
  534. return "", fmt.Errorf(errVaultRequest, err)
  535. }
  536. defer resp.Body.Close()
  537. vaultResult := vault.Secret{}
  538. if err = resp.DecodeJSON(&vaultResult); err != nil {
  539. return "", fmt.Errorf(errVaultResponse, err)
  540. }
  541. token, err := vaultResult.TokenID()
  542. if err != nil {
  543. return "", fmt.Errorf(errVaultToken, err)
  544. }
  545. return token, nil
  546. }