- or a component for Ping Identity's authentication within your Delphi application.
It has often been the rule that native applications either authenticate the user by current OS user or by an application-centric user and role model - but that might for multiple reasons not be good enough anymore.
The old AD or LDAP lookups are being replaced by cloud IAM platforms, to control and secure the authentication of the identity of the user.
After the user is authenticated (and is authorised "access" your application), the application-centric roles flow can continue as-is.
One of these IAM cloud provides is PingIdentity, and they have a fairly extensive Postman collection for their PingOne Platform API - found here. Their developer documentation is also very useful - found here.
I have previously used various ways against various providers, but it seemed that when testing against PingOne with MFA enabled (Multi-Factor Authentication - which might include the annoying phone thing - that everyone uses) - I was hit by either a CORS issue or something else.
When using an OAuth2/OpenID Connect authentication, it does require that you setup and use a redirect uri, to tell the provider who is locally listening/waiting for the "response".
The idea with this type of authentication, is that the flow is handled securely within a web browser session, and the client does at no point in time know the login credentials - only when the user is authenticated do we need an "id token". So no "local" storage/handling of "passwords".
The listener part and possible timeouts has always annoyed me, so it seemed based on the flow, that I could handle it differently by just intercepting the local redirect call with the auth code - when the user is authenticated on the PingOne side - to get the id token needed.
Implementation
The TPingOneAuth component is a descendant of standard TWebBrowser overriding the IDocHostUIHandler interface, and adding a some properties.
The GetHostInfo override is to ensure that all redirects in the browser component is triggering the OnBeforeNavigate2 event - which then on the redirect auth code call, will get the wanted id token.
begin
pInfo.cbSize := SizeOf(pInfo);
pInfo.dwFlags := 0;
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_NO3DBORDER;
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_THEME;
pInfo.dwFlags := pInfo.dwFlags or DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION;
Result := S_OK;
end;
The id token is then parsed using Pablo Rossi's brilliant JOSE and JWT library, that might as well have been done using a call to one of the PingOne's Token Introspection endpoints.
Since we do need an user id - the OpenID Connect scope must include profile also.
To parse the custom claim a custom TJWTClaims class is added, containing the profile claims we want to read.
// Adding some given by the OpenID Connect scope: profile
private
function GetPreferredUsername: string;
procedure SetPreferredUsername(const Value: string);
function GetGivenName: string;
procedure SetGivenName(const Value: string);
function GetFamilyName: string;
procedure SetFamilyName(const Value: string);
public
property PreferredUsername: string read GetPreferredUsername write SetPreferredUsername;
property GivenName: string read GetGivenName write SetGivenName;
property FamilyName: string read GetFamilyName write SetFamilyName;
end;
Usage
Install the TPingOneAuth component in the Delphi IDE, and set the library path - business as usual.
There is a small sample application in the GitHub repo, but steps are at follows:
Drop or Create the TPingOneAuth on a form, setting following properties:
- AuthEndpoint: /as/authorize
- AuthPath: https://auth.pingone.eu/
- ClientId:
- ClientSecret:
- EnvironmentId:
- RedirectUri:
- ResponseType: code
- Scope: openid profile
- TokenEndpoint: /as/token
No comments:
Post a Comment