Official Content
  • This documentation is valid for:

Below is a possible solution to the problem posed in HowTo:Using GAM in a distributed application, where back-end services lack integrated security (Integrated Security Level property = none).

Example of a service published in the business layer

In the logic layer, the service "ReturnCustomers", is a Rest service (servicio Rest, Expose property as a Web Service= TRUE, Rest Protocol = TRUE) whose code is basically as follows:

// Validate the session token received as a parameter
&ValidAccessToken = GAMRepository.ValidAccessToken(&GAMSessionToken,&GAMSession,&Errors)
if &ValidAccessToken  //If the session token received as a parameter is valid 
        //Continue with the execution flow of the program, load &CustomersSDT 
        //In the error output parameter (&GAMSessionSDT), indicate that there were no errors 
else //If the session token received as a parameter is not valid 
   //Process the error, return it in the output parameter 
endif

Rules:

parm(in:&GAMSessionToken,out:&CustomersSDT,out:&GAMSessionSDT);

Note:

  • The service receives as a parameter a &GAMSessionToken variable of GAMSessionToken type that is validated using the ValidAccessToken method of the External Object GAMRepository. If it is valid, the program flow continues as usual, and client data is obtained. Otherwise, the error is recorded in the output variable &GAMSessionSDT.
  • &GAMSessionSDT is an SDT that in this example allows recording the GAM error code.
  • The service mentioned in the example returns the clients obtained from the database in the &CustomersSDT variable.

Example of invocation of the service from the front end

On the front end, a web panel that invokes this service must do it taking into account that it is a Rest service, and build the request using HttpClient data type.
Since the service receives an input parameter that is the &GAMSessionToken, this value must be sent in the request body.

&Token = &websession.Get("USERSession") // &websession is of WebSession type; I obtain the web session
if not &Token.IsEmpty() // If the session has not expired 
//I build the body of the service request with the session token 
          &GAMSessionTokenSDT.GAMSessionToken = &Token
          &body = &GAMSessionTokenSDT.ToJson()
          //I invoke a procedure that makes the call to the Rest service 
          ProxyREST.Call(&server,&port,&baseURL,"ReturnCustomers","POST",&body,&HTTPstatus,&Result)
          //I process the service request     
         if &HTTPStatus= 200
                   &ResultSDT.FromJson(&Result)
                   &GAMSessionSDT = &ResultSDT.GAMSessionSDT
                   if &GAMSessionSDT.GAMSessionOK
                          &customersSDT = &ResultSDT.CustomersSDT
                          // I process &customer SDT containing the list of clients   
                   else
                          //The token is not valid; the login must be called   
                                                   Login.Call() 

                   endif 

          else //An error occurred when calling the service  
  endif 
else
       Login.Call()

endif

Note:

  • Regardless of whether the token is valid, if the service is correctly invoked, the HttpStatus Code is 200. Its answer, which contains the error code indicating if the session is valid or not, must be processed.
  • The SDTs defined for this example are as follows:

SampleGAMDistSDTs

The ProxyREST process makes the call to the Rest service.

&httpclient.Host= &server
&httpclient.Port = &port
&httpclient.BaseUrl = &urlbase
&httpclient.AddString(&addstring)
&httpclient.AddHeader("Content-Type","application/json") 
&httpclient.Execute(&method,&getstring)
&httpstatus = &httpclient.StatusCode
&result = &httpclient.ToString() 

​parm(&server,&port,&urlbase,&getstring,&method,&addstring,out:&httpstatus,out:&result)

Note:

  • The corresponding Content-Type must be added to the request header.

Login implementation

In the logic layer, there must be a service (that we will call "LoginService") capable of validating the username and password received, and generating a valid session token that is returned to the caller.

The following code is an example of the service:

&LoginOK = GAMRepository.Login(&username,&userpassword,&AdditionalParameters,&Errors) // &Errors is of GAMError collection type 
if not &LoginOK
       // In the error output parameter (&GAMSessionSDT) indicate that there was an error 
       &GAMSessionSDT.GAMSessionToken = ''
       &Error = &Errors.Item(1)
       &GAMSessionSDT.GAMSessionErrorCode = &Error.Code
       &GAMSessionSDT.GAMSessionError = &Error.Message
       &GAMSessionSDT.GAMSessionOK = false
else        
&GAMSession= GAMSession.Get(&Errors)
        if &Errors.Count <> 0
                 &Error = &Errors.Item(1)
                 &GAMSessionSDT.GAMSessionError = &Error.Message
                 &GAMSessionSDT.GAMSessionErrorCode = &Error.Code
                 &GAMSessionSDT.GAMSessionOK = false

        else  //There were no errors when obtaining the GAM Session; return the Token assigned to the session in the output parameter 

                &GAMSessionSDT.GAMSessionToken = &GAMSession.Token
                &GAMSessionSDT.GAMSessionOK = true

        endif
endif

Rules:

parm(in:&username,in:&userpassword,out:&GAMSessionSDT);

In the front end, the login web panel must call this service ("LoginService").

// Build the service request body to include the input parameters (&username, &userpassword)  
&UserCredentialsSDT.username = &UserName.Trim()
&UserCredentialsSDT.userpassword = &UserPassword.Trim()
&body = &UserCredentialsSDT.ToJson() 

// I invoke a procedure that makes the call to the Rest service 
ProxyREST.Call(&server,&port,&baseURL,"LoginService","POST",&body,&HTTPstatus,&Result)
if &HttpStatus = 200
    //I process the service response 
    &ResultLogin.FromJson(&Result)  //Retrieve the service response in an SDT  
    &GAMSessionSDT = &ResultLogin.GAMSessionSDT
    if &GAMSessionSDT.GAMSessionOK

           &Token = &GAMSessionSDT.GAMSessionToken //Retrieve the Token provided by the service.  
           &websession.Set("USERSession",&Token)   //Save the session token in the web session.  
           // Redirect to the caller 
     else
          //Process the error.  
     endif  
else
​      // Error in the invocation to the service; process the error.
endif  

Note:

  • In the example, the service output variable, &GAMSessionSDT, contains the error code that must be processed to provide feedback to the user. For example, its content can be as follows:
    {"GAMSessionSDT":{"GAMSessionOK":false,"GAMSessionError":"The user or password is incorrect.","GAMSessionErrorCode":"11","GAMSessionToken":""}}
  • The image below shows the SDTs defined to develop the example:

SampleSDTLogin

Remember that:

  • Using HTTPs is recommended to ensure communication through the channel.
  • The session expiry, in this case, is ruled by the WebSessionTimeout property of the Security Policy applied to the user. Read Security Session Management in Applications using GAM. Remember that since they are web services they don't have web sessions; therefore, it is independent of the server web session.
  • Remember that the application must have a logout feature, and it must run the deletion of the session on the server side.

Implementing the logout

The logout feature must be implemented on the server side.

To this end, a Rest service must be invoked to run the logout method of the GAMRepository object. In this way, the session token is invalidated in the GeneXus Access Manager (GAM).

It must contain the following code:

&er = GAMRepository.Logout(&Errors) // &Errors is a GAMError collection. The method returns a boolean value.
​//Process the errors.  

The Rest service (suppose that it is called "LogoutProcedure") is invoked with the headers ('GeneXus-Agent','SmartDevice Application') and ('Authorization','OAuth ' + Access_Token), as shown in the example below:

&httpclient.Host= &server
&httpclient.Port = &port
&httpclient.BaseUrl = &urlbase
&httpclient.AddString(&addstring) 
&tokenvalue= 'OAuth ' + &Token
&httpclient.AddHeader('Authorization',&tokenvalue)
&httpclient.AddHeader('GeneXus-Agent','SmartDevice Application')
&httpclient.AddHeader('Content-type','application/json')
&httpclient.Execute("POST","LogoutProcedure")
&httpstatus = &httpclient.StatusCode
&result = &httpclient.ToString()

Note: The logout method of the GAMRepository object invalidates the session token when its headers receive information corresponding to this token, as in this example.

The implementation of this method depends on whether the headers are received. If they are not received, it is assumed that the execution platform is a web platform, and an attempt is made to delete the web session (it is not the case in this example).

XPZ of the example:

Tcptrace and WizTools RestClient can be used for troubleshooting during development.

 

Last update: April 2024 | © GeneXus. All rights reserved. GeneXus Powered by Globant