6

We are integrating the Okta Sign-in Widget into our React-based webapp.

The example snippet:

var oktaSignIn = new OktaSignIn({baseUrl: baseUrl});
oktaSignIn.renderEl(...)

Works fine for us when rendering the widget for the first time, but after the user logs in and logs out again, the webapp renders the login component a second time and would attempt to execute the renderEl again to render the widget. This causes the following exception to be thrown:

Backbone.history has already been started

I have created this jsfiddle to demonstrate the problem. It just instantiates a signin widget twice (the second time after a wait). You can see that the second invocation causes the exception to be thrown.

https://jsfiddle.net/nudwcroo/6/

At the moment my workaround is to reload the entire webapp when going to the login component but that is undesirable for a single page app.

Is this a known issue? Is there any way to initialise the sign-in widget twice in a single javascript session?

Ian Su
  • 221
  • 1
  • 5

2 Answers2

0

Since the widget can only be instantiated once per page, it is best to hide/show the element for all Single Page Applications.

<div id="okta-login-container"></div>

<script type="text/javascript">
    var oktaSignIn = new OktaSignIn(/* config */);

    oktaSignIn.renderEl(
      { el: '#okta-login-container' },
      function (res) {
        if (res.status === 'SUCCESS') {
            // Hide element 
            $("#okta-login-container").hide();
        }
      }
    );
</script>

When you create your logout() function, make sure to show() the element instead of rendering it again.

function logout() {
    $('#okta-login-container').show();
    // Do more logic
}
jmelberg
  • 361
  • 1
  • 4
0

For those experiencing similar problems after following the Okta example provided here: (https://github.com/okta/samples-js-react/blob/master/custom-login/src/Login.jsx)

The problem is with attempting to initialize the widget multiple times within a single page application. I fixed this by only initializing the widget once at the App level, and then rendering it and removing it from the DOM when a child component mounts/unmounts.

Example:

//App.js
class App extends Component {
  constructor() {
    super()
    this.signIn = new OktaSignIn({...})
  }

  render() {
    return <SignInPage widget={this.signIn} />
  }
}

--

//SignInPage.js
...
  componentDidMount() {
    let { redirectUri } = this.state
    let { widget } = this.props
    widget.renderEl(
      { el: '#sign-in-widget' },
      (response) => {
        response.session.setCookieAndRedirect(redirectUri)
      },
      (error) => {
        throw error;
      },
    );

  }

  componentWillUnmount() {
      let { widget } = this.props
      widget.remove()
  }

  render() {
    return  <div id="sign-in-widget"/></div>
  }
slickbrick
  • 33
  • 2