Configure SSO for a service-to-service implementaton that is protected
by GIP.
About this task:In service-to-service –
also known as "machine-to-machine" – implementation, a web service authenticates itself with GIP
using its Client ID and Client Secret. GIP returns an access token with the scopes
(permissions) granting access to the web services. The access token is available via the GAS
environment at runtime, and the initial web service will send this access token to any other service
it needs to use.
Typically, you provide a web application secured by your GIP to provide the interface to
the web services, and the services are protected by the application. But in this instance, clients
may create their own applications to access your web services; therefore, you want to protect your
web service directly with the GIP, which in turn will protect other services it accesses. This is
the implementation of service-to-service described in this task.
Create the service-to-service web service
-
Configure your web service xcf file for delegation.
In the
DELEGATE
element, set the
service
attribute to the
Genero GeneroAccessService, and provide the URL of the GIP in the
IDP
element.
GeneroAccessService
is the REST web service in
$FGLDIR that
handles delegation in the service calls, and forwards the access token if needed in a call to
another service.
<APPLICATION Parent="ws.default" ...>
<EXECUTION>
</ENVIRONMENT_VARIABLE>
</PATH>
</MODULE>
<DELEGATE service="services/GeneroAccessService">
<IDP>my_OAuth_provider_url</IDP>
</DELEGATE>
#...
</EXECUTION>
</APPLICATION>
-
Register your web service as a "service-to-service" on the GIP.
- Register the details of the web service with the GIP.
- Register all the scopes (permissions) that might be granted to clients. These are the scopes you
should already have defined in your web service 4GL with the
WSScope
attribute to
provide security globally for the service (on the WSInfo
record) or for its
functions. You should have a list of such WSScope
entries.
The GIP provides the CLIENT_ID
and SECRET_ID
that you
use for requesting access tokens to the protected web services.
-
Generate stub files with code to handle OAuth access for other Genero web service you
need.
Any other web service you call from the "service-to-service" web service must be able to retrieve
the access token that is forwarded to it, and its stub file must be generated to handle OAuth as
shown in these steps.
-
Code to retrieve the access token.
You need to call the
OAuthAPI.InitService
function to initiate OAuth and
register the access
token with the GWS
engine.
Sample code to do this is shown in the
myInitServiceCall
function. In a
REST function, the access token – provided via the GAS environment at runtime – can be retrieved via
the
WSContext
variable,
ctx
in the example.
# Other web service module
IMPORT FGL OAuthAPI
PRIVATE DEFINE ctx DICTIONARY ATTRIBUTE(WSContext) OF STRING
PUBLIC FUNCTION myInitServiceCall()
DEFINE access_token STRING
# retrieve access_token
LET access_token=ctx["OIDC_ACCESS_TOKEN"]
# Init OAuth service
IF NOT OAuthAPI.InitService(5, access_token) THEN
RETURN "Cannot initiate OAuth service"
EXIT PROGRAM 1
END IF
END FUNCTION
# web service functions
-
Use the fglrestful tool with the
--oauth yes
option to
generate the stub.
(Line breaks have
been added to improve readability.)fglrestful -o ws_client
--oauth yes
http://myhost:6394/gas/ws/r/myGroup/myXcf/myClientService?openapi.json
Where:
- ws_client specifies the filename of the stub in the output
(
-o
) option.
--oauth yes
specifies that the web service is secured and code to handle OAuth
access is generated in the stub, taking the access token into account.
- ?openapi.json query string gets the OpenAPI document.
The ws_client.4gl is generated.
-
Compile ws_client.4gl.
The stub file must be imported into your "service-to-service" web service, as shown in the
next step.
-
Code in your "service-to-service" web service to get an access token.
You must call the
OAuthAPI.RetrieveServiceToken
function using the
CLIENT_ID
and
SECRET_ID
credentials of the service to request an access
token. You must provide
the credentials when calling this
function.
# Web service (registered as a "service-to-service")
IMPORT FGL ws_client # import OAuth-ready client service
IMPORT FGL OAuthAPI
DEFINE metadata OAuthAPI.OpenIDMetadataType
DEFINE token STRING
DEFINE expire INTEGER
DEFINE client_id STRING
DEFINE secret_id STRING
DEFINE scope STRING
DEFINE idp_url STRING
PUBLIC FUNCTION myGetServiceToken()
TRY
# Retrieve the access token
CALL OAuthAPI.FetchOpenIDMetadata(20, idp_url)
RETURNING metadata.*
IF metadata.issuer IS NULL THEN
ERROR "IdP not available"
EXIT PROGRAM 1
ELSE
CALL OAuthAPI.RetrieveServiceToken(5, metadata.token_endpoint, client_id, secret_id, scope )
RETURNING token, expire
IF token IS NULL THEN
DISPLAY "Unable to retrieve token"
EXIT PROGRAM 1
ELSE
DISPLAY "Access token value :",token
DISPLAY SFMT("Token expires in %1 seconds",expire)
END IF
END IF
CATCH
DISPLAY "ERROR : ",status,sqlca.sqlerrm
EXIT PROGRAM 1
END TRY
END FUNCTION
# web service functions
Troubleshooting access
- If errors are encountered accessing your service, check the logs:
vm-services-GeneroAccessService*.log, and the
AccessService.log:
For instance, if you get error 403 "Forbidden" and you
see the following message in the
AccessService.log:
ERROR : 29389 - [AccessService] "RetrieveIDPMetadata" INSERT failed :UNIQUE constraint failed: fjs_provider.issuer
Check
that the GIP's URL in the
DELEGATE
element of your web service
xcf is correct.
- If a call made to a Genero web service function fails, check the error status code returned to
the client. For example, a value of -1 indicates a failed operation. The code
-15553
with a description such as Asynchronous Connection failed
or connection timeout expired
indicates an infrastructure error. Check
that you have a call to OAuthAPI.InitService()
in your web service.
For more
information on handling errors, refer to the Handle REST server errors page in Genero Business Development Language User Guide