IFS Authentication flow with OAuth and OpenID Connect

In my previous article I wrote about Capturing IFS Aurena requests using Postman which is a shortcut method for calling IFS main cluster resources which requires OAuth2 and OpenID Connect authentication. I thought of getting little deep dive this time and try out how we can create a successful request to IFS with OAuth authentication.

What this post is about…

  • What is OAuth and how it’s used in IFS
  • Understand the OAuth 2.0 Authorization code flow
  • How to create OAuth 2.0 token for IFS Identity Provider in Postman
  • Using the OAuth 2.0 token to access IFS Main cluster resources
  • My sample app

What is OAuth?

To begin at a high level, OAuth an open standard for authorization. Today when people talks about OAuth it typically means OAuth 2.0 specification. Everything about the OAuth 2.0 standard can be found on RFC 6749 but I bet no one would ever read that. But if you are so into develop your own OAuth flow, this is the best document you can have and there are plenty of information web about this so I won’t go into details.

OAuth in IFS

With the release of Aurena client in IFS Applications 10, IFS Authentication divided into two parts.

  • OAuth2 OpenID Connect authentication for Clients
  • HTTP Basic authentication for Integrations
User authentication architecture in IFS Applications
Picture courtesy: IFS 10 Technical Documentation

When consuming IFS webservices or projections in integration cluster, we have to add the credentials in the HTTP Authorization header field of the request. There is no difference from previous versions of IFS. When consuming projections in main cluster (clients), the story is different and we need to use the OAuth2 authentication flow.

There are several variants of authorization code flow and todays’ best practice is to use PKCE (Proof Key for Code Exchange) whenever possible. What differs PKCE from regular flow is that it replaces the client secret used in the standard Authorization Code flow with a one-time code challenge. Therefore the client app doesn’t have to store a client secret.

Below diagram shows the basic steps involved with authorization code flow with PKCE.

PKCE Authorization Code Flow in IFS
PKCE Authorization Code Flow in IFS

If you need to build a new client for IFS with OAuth, above steps need to be followed to get it work. Implementing this flow could be tricky and depends on the technologies used to build the application. Using Postman, we can simulate the OAuth flow and call IFS main projections. It is also useful to check the HTTP request format requires when calling IDP endpoints so you can save a lot of time.

PKCE flow in Postman

Postman says that it supports PKCE flow and the option is available under OAuth 2.0 grant types. But I noticed that client secret is still required when getting the access token and it was discussed in this blog article as well. I’m not sure whether this is a bug on Postman or I mis-interpreted the concept.

Update 2021/01/12: This seems to be a bug in Postman and I created a issue in Postman github repo.
https://github.com/postmanlabs/postman-app-support/issues/9409

Update 2022/01/10, It’s now fixed: OAuth2.0 PKCE flow to no longer send a client_secret when it is left empty.
https://github.com/postmanlabs/postman-app-support/issues/9409#issuecomment-1008574015

We will now look at how to generate an OAuth token from Postman and call IFS main projection using it. Before that it’s interesting to check the steps for getting the IDP endpoint information (Steps 1-4 in above diagram)

Obtaining OpenID Provider Configuration

Obtaining OpenID Provider Configuration
Obtaining OpenID Provider Configuration

Open Postman and call a Main projection without authentication. You’ll get the HTTP 401 Unauthorized Request as the response. What interesting is the headers included in this response.

Response Headers in 401 unauthorized request
Response Headers in 401 unauthorized request

We can extract the IDP provider information from the WWW-Authenticate header and X-IFS-OAuth2-Resource header tells us the client ID.

According to OpenID specification, IDP provider configurations can be retrieved by calling the OpenID Connect Discovery URL. Typically, it’s in the format of

{{OPENID_CONNECT_URL}}/.well-known/openid-configuration 

From here we can get the Authorization and Token endpoints and other related details. More information can be found in OpenID Connect Discovery swagger documentation.

IDP provider configurations is a simple JSON document. Below snippet is an example discovery document received from IFS inbuilt IDP.

{
    "response_types_supported": [
        "code",
        "id_token",
        "code id_token",
        "id_token token",
        "code token",
        "code id_token token"
    ],
    "claims_supported": [
        "iss",
...
...
        "at_hash",
        "nonce"
    ],
...
...
    "scopes_supported": [
        "openid"
    ],
    "issuer": "https://IFS_URL/openid-connect-provider",
    "authorization_endpoint": "https://IFS_URL/openid-connect-provider/idp/authorization",
    "token_endpoint": "https://IFS_URL/openid-connect-provider/idp/token"
}

Now we’ll see how to obtain OAuth2 token in Postman for IFS

OAuth token in Postman

In order to get an OAuth token, you need following information

  • Authorization endpoint
  • Token endpoint
  • Client ID
  • Client Secret
  • Scope

One way to fetch above information except the client secret is to follow the steps in Obtaining OpenID Provider Configuration but all these information can be found in IFS Admin console as well.

Identity Provider details
Client ID and Secret

Go to Postman and Create a new GET request. Here I use the PartHandling projection to get PartCatalogSet information.

{{IFS_URL}}/main/ifsapplications/projection/v1/PartHandling.svc/PartCatalogSet(PartNo='TEST1')

In the Authorization tab, select OAuth 2.0 as Type

Postman Authorization
Postman Authorization

Fill the details in the OAuth 2.0 details form as follows

Add auth data to: Request Headers
Header Prefix: Bearer
Grant Type: Authorization Code (With PKCE)
Callback URL: {{IFS_URL}}/main/ifsapplications/projection/oauth2/callback
Auth URL, Access Token URL, Client ID, Client Secret: See OAuth Token step
Code Challenge Method: SHA-256
Scope: openid
Client Authentication: Send client credentials in body
OAuth 2.0 Configurations

Press Get New Access Token

It will pop up IFS login dialog to enter the credentials. Enter the credentials and Login. If everything works fine, you should get the Access token

OAuth 2.0 Token

Below diagram explains what happened underneath until we get the token

OAuth 2.0 Authorization code flow with PKCE
OAuth 2.0 Authorization code flow with PKCE

If you need to see how the HTTP requests of each step looks like, you can check the Postman console for details

OAuth 2.0 flow – Postman console

Now we face a trap where most of my friends got in trouble including me 😉

IFS requires ID token, not the access token. It’s arguable whether it’s correct approach according to the OAuth 2.0 protocol but we have to deal with it anyway. If you press Use token, Postman will use the Access token. What you need to do is, copy the ID token and paste in the Access Token field.

ID token is recognizable from Access token by it's length.

Update 2022-05-10
Tobias made a great input in one of the comments regarding how the access token can be used.
If you pass a parameter in the body of the token request called “resource” with the client id as the value, the access token is usable and the id token no longer needs to be used.

Now you are ready to call the main projection

below diagram explains the steps

Next step…

IFS will send cookies which can be used in the subsequent requests instead of the token. If you are building an app, you can keep that in mind and design the auth flow to use the cookies if they are present

IFS Session cookies

When the cookies are present, you don’t need any type of authentication. To try this, we can select Authorization to No Auth and send the request. IFS will still accept the request and send the response.

Hope you find this post useful and will add some value for IFS Aurena debugging and developments. Please comment with your thoughts on any suggestions and improvements needed.

Sample app to demo OAuth2 flow with PKCE in IFS 10

Given below is a sample .NET app I created to demonstrate the OAuth2 PKCE flow in IFS10 with IFS Identity Provider

Sample console app to demo OAuth2 flow with PKCE in IFS 10

48 thoughts on “IFS Authentication flow with OAuth and OpenID Connect

Add yours

  1. Hi Dear Damith,

    I am using restfull web service in IFS. I created a projection and wrote a function. (I wrote the details of the function in a .plsvc file)

    Information in Projection:
    action AddBusinessLeadInformation Structure(AddBusinessLeadStructure) {
    initialcheck implementation;
    parameter Name Text;
    parameter ConnectionType Text;
    parameter Email Text;
    parameter PhoneText;
    parameter ProductTip Text;
    parameter Message Text;
    }
    ——————————– FUNCTIONS —————- —————–

    ——————————– STRUCTURES —————– —————-

    structure AddBusinessLeadStructure {
    attribute Name Text;
    attribute ConnectionType Text;
    attribute Email Text;
    attribute Phone Text;
    attribute ItemTip Text;
    attribute Message Text;

    Details of my function in .plsvc:

    FUNCTION *******CrmService (
    name_ IN VARCHAR2,
    connection_type_ IN VARCHAR2,
    email_IN VARCHAR2,
    phone_ IN VARCHAR2,
    product_tip_ IN VARCHAR2,
    message_ IN VARCHAR2) RETURN Add_Business_Lead_Structure_Rec
    IS
    result_ Add_Business_Lead_Structure_Rec := Add_Business_Lead_Structure_Rec();

    p0_ VARCHAR2(32000) := NULL;

    p1_ VARCHAR2(32000) := NULL;

    p2_ VARCHAR2(32000) := NULL;

    p3_ VARCHAR2(32000) := NULL;

    phone_id_ VARCHAR2(50);

    mail_ VARCHAR2(50);

    CURSOR get_phone_no_ IS
    SELECT b.phone
    FROM ****** b
    WHERE b.phone=phone_;

    CURSOR get_mail_ IS
    SELECT b.email
    FROM ****** b
    WHERE b.email=email_;

    BEGIN

    OPEN get_phone_no_;
    FETCH get_phone_no_ INTO phone_no_ ;
    IF get_phone_no_%FOUND THEN
    error_sys.system_general(‘PhoneExists’,phone_);
    END IF;
    CLOSE get_phone_no_;

    OPEN get_mail_;
    FETCH get_mail_ INTO mail_ ;
    IF get_mail_%FOUND THEN
    error_sys.system_general(‘MailExists’,email_);
    END IF;
    CLOSE get_mail_;

    Client_sys.Add_To_Attr(‘NAME’,name_,p3_);
    Client_sys.Add_To_Attr(‘CONNECTION_TYPE’,connection_type_,p3_);
    Client_sys.Add_To_Attr(‘EMAIL’,email_,p3_);
    Client_sys.Add_To_Attr(‘PHONE’,phone_,p3_);
    Client_sys.Add_To_Attr(‘STAGE_ID’,REPLACE(NVL(product_tip_,’1+1′),’all’,’1+1′),p3_);
    Client_SYS.Add_To_Attr(‘DEFAULT_LANGUAGE’, Iso_Language_API.Decode(‘tr’), p3_);
    Client_SYS.Add_To_Attr(‘COUNTRY’, Iso_Country_API.Decode(‘TR’), p3_);
    Client_SYS.Add_To_Attr(‘TURNOVER_CURRENCY’, ‘TRY’, p3_);
    ********_API.NEW__(p0_ , p1_ , p2_ , p3_ , ‘DO’ );
    ******_API.Write_Note__(p2_, p1_ ,message_);

    ————————————————
    RETURN result_;
    END ******CrmService;

    What I want to do is: I want to add records to a table at the same time before giving a PhoneExists error. (using api) But it writes the error to the screen. It does not add the record. Is there a way? Or is there a way to return text in this rest web service?

    It returns me this as json:
    {
    “@odata.context”: “https://*************.AddBusinessLeadStructure”,
    “Name”: null,
    “ConnectionType”: null,
    “Email”: null,
    “phone”: null,
    “ProductType”: null,
    “Message”: null
    }

    Here, I can print PhoneExists as text to the json result returned. For example, if the registration is successful, it should write success. If not successful, write PhoneExists.

    I’m asking for support on the issue.

    Best regards,
    Onur

    1. Hi Onur,

      Sorry for the late reply.
      I have not tried such a scenario but was under the impression that ORA errors should return HTTP 500 with the error message in the body.
      What do you mean by writes error in the screen? Is this an integration projection or a client projection?

      I’ll try to check bit more on this and get back.

      Cheers!
      Damith

  2. Hi Dear Damith,

    Thank you very much for your information.

    You are always helping.Thank you very much for your interest.

    As a result, the best way seems to be to use the access token for a maximum of 90 days.

    If there is a problem, I will ask you again.

    Best Regards,
    Onur

  3. Hi Dear Damith,

    I have an important and urgent question.

    I gave the access token duration 31536000 in ifs. It is defined in seconds. When I convert it to days, it makes 365 days. But it expired before 5 months and fell into 401 Unauthorized error.

    Why did this happen?

    I regenerated the token.

    How can I make this period at least 1 year?

    Is there a way to automatically renew the duration?

    I need to take permanent action.

    I have asked a question about the subject before. I would be very grateful if you could provide detailed information.

    My information is as follows.

    IFS Startup Parameters:

    -Difs.oidc.accesstoken.lifetime=31536000 -Difs.oidc.refreshtoken.lifetime=31536000

    Time written in Postman:

    expires_in
    31536000

    Best Regards,
    Onur

    1. Hi Onur,

      What I’ve found in the documentation is that the maximum for access token lifetime greater than 15 minute and less than 1 day and for refresh token, maximum validity is 90 days.
      https://docs.ifs.com/techdocs/foundation1/040_administration/210_security/015_authentication/040_configure_DBIDP/default.htm

      If the tokens are not behaving as you intended, then you can report to IFS and get their input.

      But longer lifespan for token means your application security at risk. Therefore think about it when you decide the token timeout.

      If you are intend to create a SPA or other kind of app, you can use the cookie authentication to provide a continuous login experience to the users (that’s the same concept used in Aurena client as well)

      Cheers!
      Damith

    2. Hi Dear Damith,

      I asked the same question before.

      We renewed the Access token 1 day before the 3 months expired, but after 3 days, we renewed it again, isn’t it normally 3 months (90 days)? This was not an issue with the previous refresh. Why was this a problem? Can we get information about the subject?

      The problem is urgent. How can we take permanent action?

      Best Regards,
      Onur

    3. Hi Onur,

      I’m not sure how it will affect with application server restart. If you restart the application server, may be it could be the cause. Better check with IFS support regarding your question.

  4. Very helpful article.
    Thank you so much!

    Additional Information:
    If you pass a parameter in the body of the token request called “resource” with the client id as the value, the access token is usable and the id token no longer needs to be used.

      1. Hi Peter,
        Go to the Body tab in Postman request, select raw and JSOM from the type parameters and paste the json on the body section.
        Here are two examples
        1. PATCH update customer name

        2. POST incoming order confirmation

        Hope it will be helpful 🙂

  5. The values written in the document are the recommended values.
    I gave the time I wanted for the accesstoken.lifetime. Thank you very much for your information and help.

  6. Thank you very much for all your answers. You have been very helpful.
    You are giving feedback without getting bored
    I can make the Accesstoken period a maximum of 1 day.
    I mean, I want to use the token all the time.
    I want it to automatically renew after 1 day.
    I don’t want to manually create tokens every time
    Finally, we can set the refreshtoken period to a maximum of 90 days via IFS startup parameters. Is it enough to set this period?
    Sorry again, I hope I was able to explain my problem to you.

  7. Dear Damith,

    I think you understand me.

    Since I will use it permanently, the token period must be long. When the time expires, I will have to generate and use the token again.

    Best Regards,
    Onur

    1. Hi Onur, What you mean by permanent? Does that means once a user authenticates he must remain in logon without needing to login again?

      Theoretically this can be achieved by increasing refresh token lifetime. But note that it comes with a cost of security, so a wisely design you token handling flow 🙂

      Cheers!
      Damith

  8. I am using id_token as you said. There is no id_token in the list. Which should I change?

    1)ifs.oidc.authcode.lifetime
    2)ifs.oidc.accesstoken.lifetime
    3)ifs.oidc.refreshtoken.lifetime

    For example, are startup parameters written in this format in the Middleware server?

    -Difs.oidc.refreshtoken.lifetime=5184000

    If I increase the refreshtoken lifetime, will the id_token lifetime increase as well?

    1. Your format for writing the startup params in the middleware server is correct. for the ID token, I think you need to increase -Difs.oidc.accesstoken.lifetime.
      A good design would be to have access token with shorter life and a refresh token with longer life. You can save the refresh token and then use it later to get a new access token.

  9. Hi Dear Damith,

    First of all, thank you again for your support.

    How can I increase the lifetime of the id_token I get from IFS. I am using PHP language.

    Or how can I automatically refresh the id_token lifetime? Can I use refresh_token for IFS?

    Because by default it gave 3600 seconds. This is not enough. I have to use it constantly.

    I’m sorry. I asked you so many questions about this and took up your time.

    Best regards,
    Onur

    Good luck

    1. Hi Onur,

      Yes, you can use the refresh token to get a new access token. then user can access application without needing to re authentication until the refresh token lifetime. For this you have to implement the token exchange flow in your code. Since you are using PHP, hopefully you can use libraries like https://oauth2-client.thephpleague.com/

      Changing the token lifetime: Token lifetime depends on your identity provider and if you are using IFS as the identity provider, lifetime of the tokens are set in the Middleware server startup parameters. See your IFS documentation on how to set it up.

      IFS documentation path

      IFS DB IDP startup parameters

      No problem with asking questions, happy to help 🙂

      Cheers!
      Damith

  10. Hi Dear Damith,

    I tried as you showed and it worked.

    Thank you very much for your support.

    Best regards,
    Onur

    Good luck

  11. Hi Damith,

    I tried but it didn’t work.I would be very grateful if you could explain in detail.
    I am writing because the topic is very important.

    Where do I set the identity token I get in the access token field?
    Can you share as a screenshot?

    Best Regards,
    Onur

  12. Hi,

    We did the same.

    But we got the following warning.

    “The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.46) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity MAY include relevant diagnostic information. HTTP access authentication is explained in section 11”

    Can we get support about the warning?

    1. Hi Onur,

      You get the HTTP 401 with this response when your request does not have proper authentication.
      If you are using Postman, set the authentication type to OAuth 2.0 and set the ID token you received in the Access token field.

      Regards,
      Damith

      1. Hi Dear Damith,

        Can I request information on a different subject?

        I need to prepare a pdf form in IFS. But I need to print out more than one form under a single menu. The SQL queries and designs of these forms are different. For example, when I click on the instruction form menu on the screen, the form will be output according to the information on the screen and the relevant query. When I open another instruction on the same screen, it will print the relevant rdl form according to the sql query of that instruction. I can do it separately. But making it under a single menu will be very healthy for the user.

        I think it is necessary to write a condition in the .rdf file.

        How can I do that? Can you send a detailed sample code and template?

        Best regards,
        Onur

      2. Hi Onur,

        Sorry for the late reply.
        I guess your question is around printing several reports using one command?
        Yes, it’s possible to do. If you have any sample code and requirement document, please send it to my mail damithsj@gmail.com Will try to help as I can 🙂

  13. Hi,

    We did the same.But we got the following warning.

    “The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.46) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity MAY include relevant diagnostic information. HTTP access authentication is explained in section 11”

    Can we get support about the warning?

  14. Still trying to solve this issue. I now can receive a token and all checks good with regards to the token. I send the get to consume the IFS projection and I get a 401 error:

    error_description=”4886fbce-eba5-4a78-87fc-cc9f3bccbc3d: The audience claims of the provided id token indicates that this is not an intended recipient.

    Any ideas? Thanks

    1. Hi Kenneth,

      I’m not sure what cause the problem you are struggling but it could be that you have a different identity provider than IFS.
      Can you please check what’s the value for the default identity provider?

    2. Did you ever manage to solve this?
      I have a similar problem and we are using a different Identity provider than “IFS Database”

  15. error=”invalid_token”, error_description=”f5d5fc6e-8e49-4846-93b4-1c74ed8ad50f: Signature of the provided id token could not be validated against the public signing keys of the identity provider.”

    This is the error I’m seeing. Any ideas would be helpful. Thanks

  16. All is working just as you describe. I receive a token and paste it into the Access Token. When sending the the GET request an error 401 “Invalid signature in authorization header” Any ideas would be helpful. Thanks

  17. Great document!
    I am happy to that now we can use Postman to get the data from IFS, This would really help technical and functional while developing integration from/to IFS. Very Good Article!!

Leave a reply to Damith Jinasena Cancel reply

Website Powered by WordPress.com.

Up ↑