From df03b8139637215bc8f022668fc1ca67d4e687f1 Mon Sep 17 00:00:00 2001 From: Nikola Garabandic Date: Wed, 21 Aug 2024 12:27:55 +0200 Subject: [PATCH 1/3] Making the loginComplete event handler a bit more complete so that people can easily get all the necessary data for their authorization services (i.e. firebase auth) --- .../OpenID Connect/Scripts/IOidcProvider.cs | 8 ++++ .../Abstract Provider/AbstractOIDCProvider.cs | 39 ++++++++++++++++ .../Git Hub/GitHubOIDCProvider.cs | 32 ++++++++++++++ .../Google/GoogleOIDCProvider.cs | 44 +++++++++++++++++-- .../Scripts/OpenIDConnectService.cs | 14 +++--- 5 files changed, 129 insertions(+), 8 deletions(-) diff --git a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/IOidcProvider.cs b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/IOidcProvider.cs index 29511d23..5787373d 100644 --- a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/IOidcProvider.cs +++ b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/IOidcProvider.cs @@ -55,6 +55,14 @@ public interface IOidcProvider /// Returns the access token if it could be retrieved; otherwise it returns an empty string Task GetAccessTokenFromCodeAsync(string code, string redirectUri); + /// + /// Gets the complete authorization answer from the selected provider + /// + /// The authorization code + /// The redirect URI which was used during the login + /// Returns the complete authorization answer + Task GetAuthorizationAnswerAsync(string code, string redirectUri); + /// /// Gets the access token from a list of parameters in a Web answer /// diff --git a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Abstract Provider/AbstractOIDCProvider.cs b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Abstract Provider/AbstractOIDCProvider.cs index 5a7b7f1d..4d006a2e 100644 --- a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Abstract Provider/AbstractOIDCProvider.cs +++ b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Abstract Provider/AbstractOIDCProvider.cs @@ -195,6 +195,45 @@ public virtual async Task GetAccessTokenFromCodeAsync(string code, strin } } + public virtual async Task GetAuthorizationAnswerAsync(string code, string redirectUri) + { + if (ClientData == null) + { + i5Debug.LogError("No client data supplied for the OpenID Connect Client.\n" + + "Initialize this provider with an OpenID Connect Data file.", this); + return null; + } + + WWWForm form = new WWWForm(); + form.AddField("client_id", ClientData.ClientId); + form.AddField("client_secret", ClientData.ClientSecret); + form.AddField("grant_type", "authorization_code"); + form.AddField("redirect_uri", redirectUri); + form.AddField("code", code); + + Dictionary headers = new Dictionary() + { + { "Content-Type", "application/x-www-form-urlencoded" } + }; + WebResponse response = await RestConnector.PostAsync(tokenEndpoint, form.data, headers); + if (response.Successful) + { + AbstractAuthorizationFlowAnswer answer = + JsonSerializer.FromJson(response.Content); + if (answer == null) + { + i5Debug.LogError("Could not parse access token in code flow answer", this); + return null; + } + return answer; + } + else + { + Debug.LogError(response.ErrorMessage + ": " + response.Content); + return null; + } + } + /// /// Gets the access token from a list of parameters in a Web answer /// diff --git a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Git Hub/GitHubOIDCProvider.cs b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Git Hub/GitHubOIDCProvider.cs index 9209dbc8..1b032e06 100644 --- a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Git Hub/GitHubOIDCProvider.cs +++ b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Git Hub/GitHubOIDCProvider.cs @@ -62,6 +62,38 @@ public override async Task GetAccessTokenFromCodeAsync(string code, stri } } + public override async Task GetAuthorizationAnswerAsync(string code, string redirectUri) + { + if (ClientData == null) + { + i5Debug.LogError("No client data supplied for the OpenID Connect Client.\n" + + "Initialize this provider with an OpenID Connect Data file.", this); + return null; + } + + string uri = tokenEndpoint + $"?client_id={ClientData.ClientId}" + + $"&redirect_uri={redirectUri}" + $"&client_secret={ClientData.ClientSecret}&code={code}&grant_type=authorization_code"; + WebResponse response = await RestConnector.PostAsync(uri, ""); + + if (response.Successful) + { + string response_content = response.Content; + GitHubAuthorizationFlowAnswer answer = + JsonSerializer.FromJson(response_content); + if (answer == null) + { + i5Debug.LogError("Could not parse access token in code flow answer", this); + return null; + } + return answer; + } + else + { + i5Debug.LogError(response.ErrorMessage + ": " + response.Content, this); + return null; + } + } + /// /// Gets information about the logged in user from the GitHub provider /// diff --git a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs index d34d4f25..37e59faf 100644 --- a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs +++ b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs @@ -60,9 +60,7 @@ public void GenerateCSRFToken() /// Returns the access token if it could be retrieved; otherwise it returns an empty string public override async Task GetAccessTokenFromCodeAsync(string code, string redirectUri) { - redirectUri += "code?"; - - EndpointsData endpoints = await InitializeEndpointsAsync(); + await InitializeEndpointsAsync(); if (ClientData == null) { i5Debug.LogError("No client data supplied for the OpenID Connect Client.\n" + @@ -100,6 +98,46 @@ public override async Task GetAccessTokenFromCodeAsync(string code, stri } } + public override async Task GetAuthorizationAnswerAsync(string code, string redirectUri) + { + await InitializeEndpointsAsync(); + if (ClientData == null) + { + i5Debug.LogError("No client data supplied for the OpenID Connect Client.\n" + + "Initialize this provider with an OpenID Connect Data file.", this); + return null; + } + + WWWForm form = new WWWForm(); + form.AddField("code", code); + form.AddField("client_id", ClientData.ClientId); + form.AddField("client_secret", ClientData.ClientSecret); + form.AddField("redirect_uri", redirectUri); + form.AddField("grant_type", "authorization_code"); + + Dictionary headers = new Dictionary() + { + { "Content-Type", "application/x-www-form-urlencoded" } + }; + WebResponse response = await RestConnector.PostAsync(tokenEndpoint, form.data, headers); + if (response.Successful) + { + GoogleAuthorizationFlowAnswer answer = + JsonSerializer.FromJson(response.Content); + if (answer == null) + { + i5Debug.LogError("Could not parse access token in code flow answer", this); + return null; + } + return answer; + } + else + { + i5Debug.LogError(response.ErrorMessage + ": " + response.Content, this); + return null; + } + } + /// /// Extracts the authorization code from parameters of a Web answer /// diff --git a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs index a0dbc7be..48b2bbdd 100644 --- a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs +++ b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs @@ -30,6 +30,8 @@ public class OpenIDConnectService : IUpdateableService /// public string AccessToken { get; private set; } + private AbstractAuthorizationFlowAnswer _authorizationAnswer; + /// /// Is true if the user of the application is currently logged in /// @@ -62,7 +64,7 @@ public class OpenIDConnectService : IUpdateableService /// /// Event which is raised once the login was successfully completed /// - public event EventHandler LoginCompleted; + public event EventHandler LoginCompleted; /// /// Event which is reaised once the logout was completed /// @@ -246,7 +248,7 @@ public async Task GetUserDataAsync() /// Called each frame by the service manager /// Handles the redirect processing on the main thread /// - public async void Update() + public virtual async void Update() { // if we did not cache a redirect event argument: nothing to do if (eventArgs == null) @@ -267,16 +269,18 @@ public async void Update() if (OidcProvider.AuthorizationFlow == AuthorizationFlow.AUTHORIZATION_CODE) { string authorizationCode = OidcProvider.GetAuthorizationCode(eventArgs.RedirectParameters); - AccessToken = await OidcProvider.GetAccessTokenFromCodeAsync(authorizationCode, eventArgs.RedirectUri); + _authorizationAnswer = await OidcProvider.GetAuthorizationAnswerAsync(authorizationCode, eventArgs.RedirectUri); + AccessToken = _authorizationAnswer.access_token; } else { AccessToken = OidcProvider.GetAccessToken(eventArgs.RedirectParameters); + _authorizationAnswer = new AbstractAuthorizationFlowAnswer() { access_token = AccessToken}; } eventArgs = null; - if (!string.IsNullOrEmpty(AccessToken)) + if (_authorizationAnswer != null) { - LoginCompleted?.Invoke(this, EventArgs.Empty); + LoginCompleted?.Invoke(this, _authorizationAnswer); } else { From 5e59a0cb6ffa30723e420348c3a3f891b6114bc7 Mon Sep 17 00:00:00 2001 From: Nikola Garabandic Date: Wed, 21 Aug 2024 12:29:59 +0200 Subject: [PATCH 2/3] Removing redundant virtual modifier on the Update method --- .../Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs index 48b2bbdd..509445d3 100644 --- a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs +++ b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OpenIDConnectService.cs @@ -248,7 +248,7 @@ public async Task GetUserDataAsync() /// Called each frame by the service manager /// Handles the redirect processing on the main thread /// - public virtual async void Update() + public async void Update() { // if we did not cache a redirect event argument: nothing to do if (eventArgs == null) From 36622c65db5b5a056bcbc6e36e5f4b68d967e1a9 Mon Sep 17 00:00:00 2001 From: Nikola Garabandic Date: Wed, 21 Aug 2024 12:32:23 +0200 Subject: [PATCH 3/3] Removed "code?" from another place where it was being written. --- .../Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs index 37e59faf..820b653a 100644 --- a/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs +++ b/Assets/i5 Toolkit for Unity/Runtime/OpenID Connect/Scripts/OIDC Providers/Google/GoogleOIDCProvider.cs @@ -174,7 +174,6 @@ public override void OpenLoginPage(string[] scopes, string redirectUri) GenerateCSRFToken(); string responseType = AuthorizationFlow == AuthorizationFlow.AUTHORIZATION_CODE ? "code" : "token"; string uriScopes = UriUtils.WordArrayToSpaceEscapedString(scopes); - redirectUri += "code?"; string uri = authorizationEndpoint + $"?client_id={ClientData.ClientId}" + $"&response_type={responseType}" + $"&redirect_uri={redirectUri}" + $"&scope={uriScopes}" + $"&state={state}"; Browser.OpenURL(uri);