runCustomAccessTokenScript executes the custom access token script in an Otto JS VM with a 5-second execution timeout to prevent CPU exhaustion from infinite loops.
(userBytes []byte, customClaims jwt.MapClaims)
| 586 | // runCustomAccessTokenScript executes the custom access token script in an Otto JS VM |
| 587 | // with a 5-second execution timeout to prevent CPU exhaustion from infinite loops. |
| 588 | func (p *provider) runCustomAccessTokenScript(userBytes []byte, customClaims jwt.MapClaims) { |
| 589 | vm := otto.New() |
| 590 | vm.Interrupt = make(chan func(), 1) |
| 591 | |
| 592 | // Start a goroutine that will interrupt the VM after the timeout |
| 593 | done := make(chan struct{}) |
| 594 | go func() { |
| 595 | select { |
| 596 | case <-time.After(scriptTimeout): |
| 597 | vm.Interrupt <- func() { |
| 598 | panic(scriptTimeoutMsg) |
| 599 | } |
| 600 | case <-done: |
| 601 | // Script finished before timeout; goroutine exits cleanly |
| 602 | return |
| 603 | } |
| 604 | }() |
| 605 | |
| 606 | defer func() { |
| 607 | close(done) |
| 608 | if caught := recover(); caught != nil { |
| 609 | if msg, ok := caught.(string); ok && msg == scriptTimeoutMsg { |
| 610 | p.dependencies.Log.Error().Msg("custom access token script timed out after 5 seconds") |
| 611 | } else { |
| 612 | panic(caught) |
| 613 | } |
| 614 | } |
| 615 | }() |
| 616 | |
| 617 | claimBytes, _ := json.Marshal(customClaims) |
| 618 | _, _ = vm.Run(fmt.Sprintf(` |
| 619 | var user = %s; |
| 620 | var tokenPayload = %s; |
| 621 | var customFunction = %s; |
| 622 | var functionRes = JSON.stringify(customFunction(user, tokenPayload)); |
| 623 | `, string(userBytes), string(claimBytes), p.config.CustomAccessTokenScript)) |
| 624 | |
| 625 | val, err := vm.Get("functionRes") |
| 626 | if err != nil { |
| 627 | p.dependencies.Log.Debug().Err(err).Msg("error getting custom access token script") |
| 628 | } else { |
| 629 | extraPayload := make(map[string]interface{}) |
| 630 | err = json.Unmarshal([]byte(fmt.Sprintf("%v", val)), &extraPayload) |
| 631 | if err != nil { |
| 632 | p.dependencies.Log.Debug().Err(err).Msg("error converting accessTokenScript response to map") |
| 633 | } else { |
| 634 | for k, v := range extraPayload { |
| 635 | if !reservedClaims[k] { |
| 636 | customClaims[k] = v |
| 637 | } |
| 638 | } |
| 639 | } |
| 640 | } |
| 641 | } |
no test coverage detected