How to obtain the authorization code for a RestAPI - Java

Hello,

I am trying to build a Jax-RS Jersey RestApi (SP) that will integrate with Okta (IDP) using openId connect.
I have a resource /accesstoken where I try to obtain the accessToken. For the other resources I plan to give it as a parameter so that the user will be automatically logged in.

For obtaining the accesstoken, I try to do the following:

  1. Build the request URI for okta like https://dev-xxx.oktapreview.com/oauth2/default/v1/authorize?scope=openid&response_type=code&redirect_uri=restResource&state=state&client_id=clientid
  2. Read the url received after the redirect (user previously logged in)
  3. Parse the url for the authorization code
  4. Make a post request for the token using the code and the client secret
  5. Display the access token

For the moment I am stuck at step 2.
The code is something like:
AuthenticationRequest reqUri = //build auth request
HTTPResponse httpResponse = reqUri .toHTTPRequest().send();
AuthenticationResponse response = AuthenticationResponseParser.parse(httpResponse);

The last line is failing because the httpResonse has no headers with the location.
If I paste in browser the reqUri, I am correctly redirected to my app and the url with the access code is diplayed in browser, as well as in browser’s headers.

Anyone seen this before or knows how to solve it?
Thanks in advance!

If you are trying to build a JAX-RS REST API, you might want to look at implementing a Resource Server. If you have a UI component in your application an authorization code flow might be your best bet. I mention this because a Resource Server is much easier to implement (as obtaining the access token would be handled by a different application), and you could use something like the Okta JWT Verifier for this, or make a call to Okta’s Introspect endpoint.

We also have a Dropwizard based blog post which should mostly be compatible with other JAX-RS implementations.

If you give us specifics on the frameworks you are using we might be able to recommend another OAuth/OIDC lib.

Let us know!

Thanks for the answer!
I used the wrong terminology (SP - service provider as in SAML SSO). I think the correct name is Resource Server.
Unfortunately, we have a request for later use without a browser component.
I was trying to implement only the get for access token to require to use the browser (for being able to handle the redirects) and then to pass the access token as parameter for all other resource requests.
For communicating with okta api I’m using

com.nimbusds
nimbus-jose-jwt
6.0


com.nimbusds
oauth2-oidc-sdk
6.0

Can you create an example project and stick it up on GitHub? We can probably take a look.

I’m afraid I don’t know how to create a project from scratch to exemplify the issue and the actual project I’m trying to enhance is too big
I’ll paste here the class,:

package <path>;

import <path>.BaseResource;

import com.nimbusds.oauth2.sdk.AuthorizationCode;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.ResponseType;
import com.nimbusds.oauth2.sdk.Scope;
import com.nimbusds.oauth2.sdk.http.HTTPResponse;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.State;
import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;
import com.nimbusds.openid.connect.sdk.Nonce;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

/**
 * Test resource.
 *
 */
@Path("/v1/accesstoken")
public class AccessToken extends BaseResource
{
	static final String CLIENT_ID = "<>";
	static final String REDIRECT_URI = "https://<localserver:port>/v1/accesstoken";
	static final String AUTHORIZATION_URI = "https://dev-<id>.oktapreview.com/oauth2/default/v1/authorize";
	public AccessToken()
	{
	}

	/**
	 * Test method.
	 * @return accessToken
	 */
	@GET
	@Produces(MediaType.TEXT_PLAIN)
	public String getAccessToken() throws URISyntaxException, IOException, ParseException
	{
		AuthorizationCode code = getAuthCode();
		return obtainAccessToken(code);
	}

	private String obtainAccessToken(AuthorizationCode code)
	{
		//TODO
		return "";
	}

	public AuthorizationCode getAuthCode() throws IOException, URISyntaxException, ParseException
	{
		ClientID clientID = new ClientID(CLIENT_ID);
		URI authUri = new URI(AUTHORIZATION_URI);
		URI callback = new URI(REDIRECT_URI);
		State state = new State();
		Nonce nonce = new Nonce();

        // Compose the request (in code flow)
		AuthenticationRequest req = new AuthenticationRequest(
				authUri,
				new ResponseType("code"),
				Scope.parse("openid"),
				clientID,
				callback,
				state,
				nonce);

		HTTPResponse httpResponse = req.toHTTPRequest().send();
		AuthenticationResponse response = AuthenticationResponseParser.parse(httpResponse);
		//here I got the error because of the missing location header

		if (response instanceof AuthenticationErrorResponse) {
			// TODO process error
		}

		AuthenticationSuccessResponse succesResponse = (AuthenticationSuccessResponse)response;
		// Retrieve the authorisation code
		AuthorizationCode code = succesResponse.getAuthorizationCode();

		// check the state
		if(succesResponse.getState().equals(state)){
			//TODO
		} else {
			//TODO
		}
		return code;
	}

}

What does the response body that causes the “parse” method look like? Is it JSON or HTML? Can you include that too?

It’s html. The Okta login

<!DOCTYPE html>
<!--[if IE 7]><html class="lt-ie10 lt-ie9 lt-ie8"><![endif]-->
<!--[if IE 8]><html class="lt-ie10 lt-ie9"> <![endif]-->
<!--[if IE 9]><html class="lt-ie10"><![endif]-->
<!--[if gt IE 9]><html><![endif]-->
<!--[if !IE]><!--><html><!--<![endif]-->
<head>

    <script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>

    <script type="text/javascript">
  
  window.oktaCdnFailover = (function() {
    var oktaScriptLoadErrors = [];
    window.addEventListener('load', function() {
      if (oktaScriptLoadErrors.length === 0) {
        return;
      }

      // Log only if all the scripts failed to load
      var scripts = document.querySelectorAll('script');
      var scriptsCount = 0;
      for (var i = 0; i < scripts.length; i++) {
        if (scripts[i].src && scripts[i].src.indexOf('mixpanel') === -1) {
          scriptsCount++;
        }
      }

      if (scriptsCount != oktaScriptLoadErrors.length) {
        return;
      }

      if (window.localStorage) {
        window.localStorage.setItem('oktaScriptLoadErrors', oktaScriptLoadErrors.join(','));
      }

      var errorMessage = 'page:' + window.location.href + '|';
      errorMessage += oktaScriptLoadErrors.join('|');

      var dataObj = {message: errorMessage, type: 'error'};
      var xhr = new XMLHttpRequest();
      xhr.open('POST', '/api/internal/client-logging/script-load-fail');
      xhr.setRequestHeader('Content-Type', 'application/json');
      xhr.send(JSON.stringify(dataObj));
    });
    return function(script) {
      if (script.indexOf('mixpanel') > -1) {
        return;
      }
      oktaScriptLoadErrors.push(script);
    };
  })();
  
  window.oktaCdnSuccess = (function() {
    var oktaScriptLoadSuccess = [];
    window.addEventListener('load', function() {
      if(oktaScriptLoadSuccess.length === 0) {
        return;
      }

      var successMessage = 'page:' + window.location.href + '|';
      successMessage += oktaScriptLoadSuccess.join('|');

      var dataObj = {message: successMessage, type: 'info'};
      var xhr = new XMLHttpRequest();
      xhr.open('POST', '/api/internal/client-logging/script-load-fail');
      xhr.setRequestHeader('Content-Type', 'application/json');
      xhr.send(JSON.stringify(dataObj));
    });
    return function(script) {
      if (!window.localStorage) {
        return;
      }
      var storedScriptErrors = window.localStorage.getItem('oktaScriptLoadErrors');
      if (!(storedScriptErrors && storedScriptErrors.indexOf(script) > -1)) {
        return;
      }
      oktaScriptLoadSuccess.push(script);
      var storedScriptErrorsArray = storedScriptErrors.split(',')
      storedScriptErrorsArray.splice(storedScriptErrorsArray.indexOf(script), 1)
      window.localStorage.setItem('oktaScriptLoadErrors', storedScriptErrorsArray.join(','));
    };
  })();
</script>

<script src="https://op1static.oktacdn.com/assets/js/widget/testscript.8b00a0599e8d731970eae85a11c92d4a.js?v=1" crossorigin="anonymous" integrity="sha384-Lhsa8qaDx4gNblcOUQPJSmKlvBQat00JSwBIs9hfhSBfyHikHbioeI+69/7jB5dP" onerror="oktaCdnFailover && oktaCdnFailover(this.src);" onload="oktaCdnSuccess && oktaCdnSuccess(this.src);" type="text/javascript"></script><title>Axway-dev-615108 - Sign In</title>
        <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="robots" content="none" />

    <link href="https://op1static.oktacdn.com/assets/loginpage/css/okta-login-page.min.c2335d687406691ab0663072de302c86.css" type="text/css" rel="stylesheet"/><script>
        var okta = {
            locale: 'en',
            deployEnv: 'PREVIEW'
        };
    </script>
    <script>window.okta || (window.okta = {}); okta.cdnUrlHostname = "//op1static.oktacdn.com"; okta.cdnPerformCheck = false;</script><script>window.okta || (window.okta = {});window.okta.mixpanel = true;window.okta.mixpanelTrackingSamplingFactors = {"_DEFAULT":1.0};</script><script>if (window.module) module = window.module;</script>

</head>
<body class="auth okta-container">

<!--[if gte IE 8]>
  <![if lte IE 9]>

    <style>
    .unsupported-browser-banner-wrap {
      padding: 20px;
      border: 1px solid #ddd;
      background-color: #f3fbff;
    }
    .unsupported-browser-banner-inner {
      position: relative;
      width: 735px;
      margin: 0 auto;
      text-align: left;
    }
    .unsupported-browser-banner-inner .icon {
      vertical-align: top;
      margin-right: 20px;
      display: inline-block;
      position: static !important;
    }
    .unsupported-browser-banner-inner a {
      text-decoration: underline;
    }
    </style>

    <div class="unsupported-browser-banner-wrap">
      <div class="unsupported-browser-banner-inner">
        <span class="icon icon-16 icon-only warning-16-yellow"></span>You are using an unsupported browser. For the best experience, update to <a href="https://support.okta.com/help/articles/Knowledge_Article/24532952-Platforms---Browser-and-OS-Support">a supported browser</a>.</div>
    </div>

  <![endif]>
<![endif]-->
<!--[if IE 8]> <div id="login-bg-image-ie8" class="login-bg-image" data-se="login-bg-image"></div> <![endif]-->
<!--[if (gt IE 8)|!(IE)]><!--> <div id="login-bg-image" class="login-bg-image" data-se="login-bg-image"></div> <!--<![endif]-->

<!-- hidden form for reposting fromURI for X509 auth -->
<form action="/login/cert" method="post" id="x509_login" name="x509_login" style="display:none;">
    <input type="hidden" class="hide" name="_xsrfToken" value="null"/><input type="hidden" id="fromURI" name="fromURI" class="hidden" value="&#x2f;oauth2&#x2f;v1&#x2f;authorize&#x2f;redirect&#x3f;okta_key&#x3d;EMoabW_s8Ch-lMotkd7Niz651Cz2Q-Oo4EExqOIeOQE"/>
</form>

<div class="content">
  <style type="text/css">
    .noscript-msg {
        background-color: #fff;
        border-color: #ddd #ddd #d8d8d8;
        box-shadow:0 2px 0 rgba(175, 175, 175, 0.12);
        text-align: center;
        width: 398px;
        min-width: 300px;
        margin: 200px auto;
        border-radius: 3px;
        border-width: 1px;
        border-style: solid;
    }

    .noscript-content {
        padding: 42px;
    }

    .noscript-content h2 {
        padding-bottom: 20px;
    }

    .noscript-content h1 {
        padding-bottom: 25px;
    }

    .noscript-content a {
        background: transparent;
        box-shadow: none;
        display: table-cell;
        vertical-align: middle;
        width: 314px;
        height: 50px;
        line-height: 36px;
        color: #fff;
        background: linear-gradient(#007dc1, #0073b2), #007dc1;
        border: 1px solid;
        border-color: #004b75;
        border-bottom-color: #00456a;
        box-shadow: rgba(0, 0, 0, 0.15) 0 1px 0, rgba(255, 255, 255, 0.1) 0 1px 0 0 inset;
        -webkit-border-radius: 3px;
        border-radius: 3px;
    }

    .noscript-content a:hover {
        background: #007dc1;
        cursor: hand;
        text-decoration: none;
    }
</style>
<noscript>
    <div id="noscript-msg" class="noscript-msg">
        <div class="noscript-content">
            <h2>Javascript is required</h2>
            <h1>Javascript is disabled on your browser.&nbspPlease enable Javascript and refresh this page.</h1>
            <a href=".">Refresh</a>
        </div>
    </div>
</noscript>
<div id="signin-container"></div>
  <div id="okta-sign-in" class="auth-container main-container" style="display:none">
      <div id="unsupported-onedrive" class="unsupported-message" style="display:none">
        <h2 class="o-form-head">Your OneDrive version is not supported</h2>
        <p>Upgrade now by installing the OneDrive for Business Next Generation Sync Client to login to Okta</p>
        <a class="button button-primary" target="_blank" href="https://support.okta.com/help/articles/Knowledge_Article/Upgrading-to-OneDrive-for-Business-Next-Generation-Sync-Client">
          Learn how to upgrade</a>
      </div>
      <div id="unsupported-cookie" class="unsupported-message" style="display:none">
          <h2 class="o-form-head">Cookies are required</h2>
          <p>Cookies are disabled on your browser. Please enable Cookies and refresh this page.</p>
          <a class="button button-primary" target="_blank" href=".">
              Refresh</a>
      </div>
  </div>
</div>

<div class="footer">
  <div class="footer-container clearfix">
    <p class="copyright">Powered by <a href="http://www.okta.com/" class="inline-block notranslate">Okta</a></p>
        <p class="privacy-policy"><a href="/privacy" target="_blank" class="inline-block margin-l-10">Privacy Policy</a></p>
    </div>
</div>

<script type="text/javascript">function runLoginPage (fn) {var mainScript = document.createElement('script');mainScript.src = 'https://op1static.oktacdn.com/assets/js/mvc/loginpage/initLoginPage.pack.28480ea192eb1871ce16e253fbd87728.js?v=1';mainScript.crossOrigin = 'anonymous';mainScript.integrity = 'sha384-zlXzmQsLIV6NfOu8db2zLQ5tbtyF8HkzKKZZS5/Bf4xrHMGVVtwdCSZ2l+tcGqQu';mainScript.onerror = function() { oktaCdnFailover && oktaCdnFailover(this.src) };mainScript.onload = function() { oktaCdnSuccess && oktaCdnSuccess(this.src) };document.getElementsByTagName('head')[0].appendChild(mainScript);fn && mainScript.addEventListener('load', function () { setTimeout(fn, 1) });}</script><script type="text/javascript">
(function(){

  var baseUrl = 'https\x3A\x2F\x2Fdev\x2D615108.oktapreview.com';
  var suppliedRedirectUri = '';
  var repost = false;
  var stateToken = '';
  var fromUri = '\x2Foauth2\x2Fv1\x2Fauthorize\x2Fredirect\x3Fokta_key\x3DEMoabW_s8Ch\x2DlMotkd7Niz651Cz2Q\x2DOo4EExqOIeOQE';
  var username = '';
  var rememberMe = true;
  var smsRecovery = false;
  var callRecovery = false;
  var emailRecovery = true;
  var usernameLabel = 'Username';
  var usernameInlineLabel = '';
  var passwordLabel = 'Password';
  var passwordInlineLabel = '';
  var signinLabel = 'Sign\x20In';
  var forgotpasswordLabel = 'Forgot\x20password\x3F';
  var unlockaccountLabel = 'Unlock\x20account\x3F';
  var helpLabel = 'Help';
  var orgSupportPhoneNumber = '';
  var hideSignOutForMFA = false;
  var loginPageUrlRedirect = '';
  var enableUrlFixForEmbeddedBrowsers = false;
  var footerHelpTitle = 'Need\x20help\x20signing\x20in\x3F';
  var recoveryFlowPlaceholder = 'Email\x20or\x20Username';
  var signOutUrl = '';
  var authScheme = 'OAUTH2';

  var securityImage = true;
  

  var windowsVerify = false;
  
    windowsVerify = true;
  

  var selfServiceUnlock = false;
  

  var preventBrowserFromSavingOktaPassword = false;
  

  var enableMixpanelTracking = false;
  

  var autoPush = false;
  
    autoPush = true;
  

  var publishToAccountChooser = false;
  var accountChooserDiscoveryUrl = null;
  
    publishToAccountChooser = true;
    accountChooserDiscoveryUrl = 'https://login.okta.com/discovery/iframe.html';
  

  // In case of custom app login, the uri is already absolute, so we must not attach baseUrl
  var redirectUri;
  if (isAbsoluteUri(fromUri)) {
      redirectUri = fromUri;
  } else {
      redirectUri = baseUrl + fromUri;
  }
  

  var customButtons;
  

  var customLinks = [];
  

  var linkParams;
  

  var idpDiscovery;
  var idpDiscoveryRequestContext;
  

  var hasPasswordlessPolicy = false;
  

  var showPasswordToggleOnSignInPage = false;
  

  var hasOAuth2ConsentFeature = false;
  var consentFunc;
  

  var hasMfaAttestationFeature = false;
  

  var registration = false;
  

  var webauthn = false;
  

  var loginPageConfig = {
    fromUri: fromUri,
    repost: repost,
    redirectUri: redirectUri,
    isMobileClientLogin: false,
    isMobileSSO: false,

    linkParams: linkParams,
    hasChromeOSFeature: false,
    showLinkToAppStore: false,
    publishToAccountChooser: publishToAccountChooser,
    accountChooserDiscoveryUrl: accountChooserDiscoveryUrl,
    preventBrowserFromSavingOktaPassword: preventBrowserFromSavingOktaPassword,
    enableMixpanelTracking: enableMixpanelTracking,
    enableUrlFixForEmbeddedBrowsers: enableUrlFixForEmbeddedBrowsers,
    loginPageUrlRedirect: loginPageUrlRedirect,
    mfaAttestation: hasMfaAttestationFeature,
    signIn: {
      el: '#signin-container',
      baseUrl: baseUrl,
      logo: 'https://op1static.oktacdn.com/assets/img/logos/okta-logo.00b28e552573899e15fa6e77278759d5.png',
      logoText: 'Axway\x2Ddev\x2D615108',
      helpSupportNumber: orgSupportPhoneNumber,
      stateToken: stateToken,
      username: username,
      signOutLink: signOutUrl,
      consent: consentFunc,
      authScheme: authScheme,
      relayState: fromUri,
      idpDiscovery: {
        requestContext: idpDiscoveryRequestContext
      },
      features: {
        router: true,
        securityImage: securityImage,
        rememberMe: rememberMe,
        autoPush: autoPush,
        webauthn: webauthn,
        smsRecovery: smsRecovery,
        callRecovery: callRecovery,
        emailRecovery: emailRecovery,
        windowsVerify: windowsVerify,
        selfServiceUnlock: selfServiceUnlock,
        multiOptionalFactorEnroll: true,
        deviceFingerprinting: false,
        trackTypingPattern: false,
        hideSignOutLinkInMFA: hideSignOutForMFA,
        customExpiredPassword: true,
        idpDiscovery: idpDiscovery,
        passwordlessAuth: hasPasswordlessPolicy,
        consent: hasOAuth2ConsentFeature,
        showPasswordToggleOnSignInPage: showPasswordToggleOnSignInPage,
        registration: registration
      },

      assets: {
        baseUrl: okta.cdnUrlHostname + '/assets/js/mvc/loginpage/i18n'
      },

      language: okta.locale,
      i18n: {},

      customButtons: customButtons,

      helpLinks: {
        help: '',
        forgotPassword: '',
        unlock: '',
        custom: customLinks
      }
    }
  };

  loginPageConfig.signIn.i18n[okta.locale] = {
    
    'primaryauth.username.placeholder': usernameLabel,
    'primaryauth.username.tooltip': usernameInlineLabel,
    'primaryauth.password.placeholder': passwordLabel,
    'primaryauth.password.tooltip': passwordInlineLabel,
    'primaryauth.title': signinLabel,
    'forgotpassword': forgotpasswordLabel,
    'unlockaccount': unlockaccountLabel,
    'help': helpLabel,
    'needhelp': footerHelpTitle,
    'password.forgot.email.or.username.placeholder': recoveryFlowPlaceholder,
    'password.forgot.email.or.username.tooltip': recoveryFlowPlaceholder,
    'account.unlock.email.or.username.placeholder': recoveryFlowPlaceholder,
    'account.unlock.email.or.username.tooltip': recoveryFlowPlaceholder
  };

  function isOldWebBrowserControl() {
    // We no longer support IE7. If we see the MSIE 7.0 browser mode, it's a good signal
    // that we're in a windows embedded browser.
    if (navigator.userAgent.indexOf('MSIE 7.0') === -1) {
      return false;
    }

    // Because the userAgent is the same across embedded browsers, we use feature
    // detection to see if we're running on older versions that do not support updating
    // the documentMode via x-ua-compatible.
    return document.all && !window.atob;
  }

  function isAbsoluteUri(uri) {
    var pat = /^https?:\/\//i;
    return pat.test(uri);
  }

  var unsupportedContainer = document.getElementById('okta-sign-in');

  // Old versions of WebBrowser Controls (specifically, OneDrive) render in IE7 browser
  // mode, with no way to override the documentMode. In this case, inform the user they need
  // to upgrade.
  if (isOldWebBrowserControl()) {
    document.getElementById('unsupported-onedrive').removeAttribute('style');
    unsupportedContainer.removeAttribute('style');
  }
  else if (!navigator.cookieEnabled) {
    document.getElementById('unsupported-cookie').removeAttribute('style');
    unsupportedContainer.removeAttribute('style');
  }
  else {
    unsupportedContainer.parentNode.removeChild(unsupportedContainer);
    runLoginPage(function () {
      OktaLogin.initLoginPage(loginPageConfig);
    });
  }

}());
</script>

<script>
  window.addEventListener('load', function(event) {
    function applyStyle(id, style) {
      if (style) {
        var el = document.getElementById(id);
        if (el) {
          el.setAttribute('style', style);
        }
      }
    }
    applyStyle('login-bg-image', "background-image: none");
    applyStyle('login-bg-image-ie8', "");
  });
</script>

</body>
</html>

I think this lib is missing something between these two lines:

HTTPResponse httpResponse = req.toHTTPRequest().send();

AuthenticationResponse response = AuthenticationResponseParser.parse(httpResponse);

You need to redirect the user to the login page, not do this from your server application, does that AuthenticationRequest class have any methods to build a URL that you could use to send a redirect?

Yes. The req variable holds the url that is built. If I paste that url in the browser, it does the redirect to the okta login page (if user is not logged in) and then returns the authorization code if the user is successfully logged in.
But I guess I’m not using it correctly by doing that simple send().
Thank you! I will look some more into that area.

1 Like

Hi,

Have you resolved the problem? How? Thank you
I need your help.

We abandoned that project, but someone told me in the meantime that using
HTTPResponse httpResponse = req.toHTTPRequest().send();
is incorrect because this is for is a regular request and instead a redirect is needed.
e.g.
Response autorizationURT = Response.seeOther(buildAuthorizationCodeRequest(state, nonce, clientID, redirectUri)).build();