How can I get callback data after permission page in unity3D - c#

I tried to programming the Fitbit authorization process but it need some browser issue that I want to avoid them.
Redirect URL: http://localhost
public void GoToAuthorizePage() => Application.OpenURL("https://www.fitbit.com/oauth2/authorize...");
For this purpose, the client must first be referred to the authorize link, and after the permission is confirmed by the user, the authorize code will be sent in the callback header. However, it is difficult for me to get callback information because it's depended on browser behavior, maybe something like this that works..
public static async void GetLocalHostInfo()
{
var apiPath = "http://localhost/+#info?!!";
var request = UnityWebRequest.Get(apiPath);
await SendRequest(request);
authorizeCode = request.downloadHandler.text.ToAutorizeCode();
}
Type of redirect callback
More information on fitbitautorization:
https://dev.fitbit.com/build/reference/web-api/developer-guide/authorization/

Related

How can I Get a header in localhost after the permissions list check?

Here I have a game that needs to be connected to the Fitbit account. To do this, when I press the Authorize button, I said to open the authorize URl in the Fitbit page.
public void GoToAuthorizePage() => Application.OpenURL(Request.URLAuthorize);
public const string URLAuthorize = "https://www.fitbit.com/oauth2/authorize?response_typ..." // In Request class
The permission page opens properly in Microsoft Edge and after login and permissions check, it refers me to localHost. At the top of the localhost header I can see the code and copy it into the project to get the original AccessToken through it.
Here I can manually copy the header Authorization Code and get AccessToken after entering it in the InputField below, but my problem is that I want to remove this manual copying process and move the code to the unit itself after sending the authorization code in the local host header.
What I want is an Async method that takes the information sent to the local host header. But I don't know how? I think the wait code for the header should be like this, but it doesn't really work.
public async void GoToAuthorizePage()
{
Application.OpenURL(Request.URLAuthorize);
var _requestHeader = UnityWebRequest.Get("https://localhost");
_requestHeader.SendWebRequest();
while (!_requestHeader.isDone) Task.Yield();
var result = _requestHeader.GetRequestHeader("Code");
}
Fitbit API docs: https://dev.fitbit.com/build/reference/web-api/authorization/

Using RemoteAuthenticationHandler CallbackPath with IApplicationBuilder path match

Related questions
Using IApplicationBuilder.Map generates nested paths with UseMvc
CallbackPath implementation
Redirect URI with Google using asp.net MVC
How to configure ASP.net Core server routing for multiple SPAs hosted with SpaServices
Problem
I have a service running under a specific path on a domain, e.g. https://www.example.com/myservice. The myservice path is dedicated to my service and other services have other paths at the same domain. It is setup like this in startup configure:
app.Map("/myservice", builder =>
{
builder.UseStaticFiles();
builder.UseMvcWithDefaultRoute();
});
I am using a library that implements a custom RemoteAuthenticationHandler. By default, the callback path routes to /x-callback which results in the browser trying to access https://www.example.com/x-callback.
Since my service does not process url's without the /myservice prefix I get a 404. Changing the URL in the browser to /myservice/x-callback manually loads the callback and all is fine.
I can set the callback path for the handler in startup options as expected in startup configure services.
services.AddSomething(options =>
{
options.AddThingX((o) =>
{
o.CallbackPath = new PathString($"/myservice{o.CallbackPath}");
});
});
When I set the callback path like that the browser tries to load /myservice/x-callback. But, this URL now returns a 404. It seems the handler for the callback also has its URL changed. Changing the URL in the browser to /myservice/myservice/x-callback loads the callback as expected.
The RemoteAuthenticationHandler
This is the code in the handler that handles the challenge and uses the callback path. It sets the callback path as a query parameter to the login url.
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
// Add options etc
// ...
// ...
// This defines the login url, with a query parameter for the CallbackPath
var loginUrl = GetLoginUrl(loginOptions);
Response.Redirect(loginUrl);
return Task.CompletedTask;
}
private string GetLoginUrl(MyServiceLoginOptions loginOptions)
{
// This is where the return url is set. The return url
// is used after login credentials are verified.
return $"{Options.LoginPath}" +
$"?returnUrl={UrlEncoder.Encode(Options.CallbackPath)}" +
$"&loginOptions={UrlEncoder.Encode(_loginOptionsProtector.Protect(loginOptions))}";
}
The login controller
This is where the user can provide the credentials and have them verified. After verification, the user is redirected to the callback path.
private async Task<ActionResult> ChallengeComplete(LoginStatusRequest request, ChallengeResponse challengeResponse)
{
// auth logic
// ...
// All is fine, the users credentials have been verified. Now
// we can redirect to the CallbackPath.
return Ok(Response.Finished(returnUri));
}
Note
I could do a URL rewrite but if possible, I would like to use the "correct" /myservice path to avoid confusion and perhaps causing issues for other services (though very unlikely).
Question
How can I prefix the callback path with /myservice so my application can process it without also adding the duplicate prefix?
MapMiddleware is adding the matched path to the Request.PathBase, so you can use it when creating the return url
string returnUrl = Context.Request.PathBase + Options.CallbackPath;

Redirect after cookie validation fails in ASP.NET Core

I have implemented a cookie validator as described in the ASP.NET docs:
public static class CookieValidator
{
public static async Task ValidateAsync(CookieValidatePrincipalContext context)
{
...
if (invalidCookie)
{
context.RejectPrincipal();
await context.HttpContext.Authentication.SignOutAsync("MyCookieScheme");
}
}
}
It seems to be working correctly and gets into the invalidCookie block, rejecting the principal and signing out. After that I would like to redirect to a different URL. How do I have it redirect if I invalidate the cookie?
It should already return a 302 and add a 'location' header to your response with either the 'AccessDeniedPath' or 'LoginPath' when you reject principal, however lets say you want to add a header change the status code, or other customization.
In the invalidCookie block you can set custom headers with the following.
context.httpContext.Response.Headers["forceRedirect"] = httpContext.Request.Host.ToString();
Then when you get the response back on the client you need to check if this header is present, if it is then you redirect to the link given. This allows you to at least set headers, however if you changed the status code in the same place the framework will still override the status code when you reject principal (after you are out of the ValidateAsync function).
In order to do this, and stop the framework from overriding your custom response you have to declare an onRedirectToAccessDenied funtion.
In startup.cs: ConfigureServices
services.ConfigureApplicationCookie(options => { //this just makes checks against the cookie, so if the user deletes the cookie then ValidateAsync never fires
// Cookie settings ...
options.Events.OnRedirectToAccessDenied = CookieValidator.overrideRedirect;
options.Events.OnValidatePrincipal = CookieValidator.ValidateAsync;
});
Then in CookieValidator:
It is worth noting that if you do this then other rejections will execute this code too. Like if you also have a Roles authorization then when the user fails that authorization it will call this. This can be frustrating if you want the code to return different responses based upon why auth failedI am solving this by using policies and having them return the custom responses because it knows exactly why it failed then have overrideRedirect empty so the framework doesn't change my response.
internal static async Task overrideRedirect(RedirectContext<CookieAuthenticationOptions> context) {
...
//In here you can customize the response anyway you want and it will not be changed
}
It should be noted that ValidateAsync only is called if the cookie is present in the request, so if the user deletes the cookie then they can skip this validation. In my case I got around this by validating the cookie here, but still had other auth code (i added policy requirements in an IAuthorizationHandler) later that verified that the user had a claim. If the user deleted their cookie then the claim wouldn't be there and they would still lose access.

Google Data API Authorization Redirect URI Mismatch

Background
I am wanting to write a small, personal web app in .NET Core 1.1 to interact with YouTube and make some things easier for me to do and I am following the tutorials/samples in Google's YouTube documentation. Sounds simple enough, right? ;)
Authenticating with Google's APIs seems impossible! I have done the following:
Created an account in the Google Developer Console
Created a new project in the Google Developer Console
Created a Web Application OAuth Client ID and added my Web App debug URI to the list of approved redirect URIs
Saved the json file provided after generating the OAuth Client ID to my system
In my application, my debug server url is set (and when my application launches in debug, it's using the url I set which is http://127.0.0.1:60077).
However, when I attempt to authenticate with Google's APIs, I recieve the following error:
That’s an error.
Error: redirect_uri_mismatch
The redirect URI in the request, http://127.0.0.1:63354/authorize/,
does not match the ones authorized for the OAuth client.
Problem
So now, for the problem. The only thing I can find when searching for a solution for this is people that say
just put the redirect URI in your approved redirect URIs
Unfortunately, the issue is that every single time my code attempts to authenticate with Google's APIs, the redirect URI it is using changes (the port changes even though I set a static port in the project's properties). I cannot seem to find a way to get it to use a static port. Any help or information would be awesome!
NOTE: Please don't say things like "why don't you just do it this other way that doesn't answer your question at all".
The code
client_id.json
{
"web": {
"client_id": "[MY_CLIENT_ID]",
"project_id": "[MY_PROJECT_ID]",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "[MY_CLIENT_SECRET]",
"redirect_uris": [
"http://127.0.0.1:60077/authorize/"
]
}
}
Method That Is Attempting to Use API
public async Task<IActionResult> Test()
{
string ClientIdPath = #"C:\Path\To\My\client_id.json";
UserCredential credential;
using (var stream = new FileStream(ClientIdPath, FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeReadonly },
"user",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var channelsListRequest = youtubeService.Channels.List("contentDetails");
channelsListRequest.Mine = true;
// Retrieve the contentDetails part of the channel resource for the authenticated user's channel.
var channelsListResponse = await channelsListRequest.ExecuteAsync();
return Ok(channelsListResponse);
}
Project Properties
The Original Answer works, but it is NOT the best way to do this for an ASP.NET Web Application. See the update below for a better way to handle the flow for an ASP.NET Web Application.
Original Answer
So, I figured this out. The issue is that Google thinks of a web app as a JavaScript based web application and NOT a web app with server side processing. Thus, you CANNOT create a Web Application OAuth Client ID in the Google Developer Console for a server based web application.
The solution is to select the type Other when creating an OAuth Client ID in the Google Developer Console. This will have Google treat it as an installed application and NOT a JavaScript application, thus not requiring a redirect URI to handle the callback.
It's somewhat confusing as Google's documentation for .NET tells you to create a Web App OAuth Client ID.
Feb 16, 2018 Updated Better Answer:
I wanted to provide an update to this answer. Though, what I said above works, this is NOT the best way to implement the OAuth workflow for a ASP.NET solution. There is a better way which actually uses a proper OAuth 2.0 flow. Google's documentation is terrible in regards to this (especially for .NET), so I'll provide a simple implementation example here. The sample is using ASP.NET core, but it's easily adapted to the full .NET framework :)
Note: Google does have a Google.Apis.Auth.MVC package to help simplifiy this OAuth 2.0 flow, but unfortunately it's coupled to a specific MVC implementation and does not work for ASP.NET Core or Web API. So, I wouldn't use it. The example I'll be giving will work for ALL ASP.NET applications. This same code flow can be used for any of the Google APIs you've enabled as it's dependent on the scopes you are requesting.
Also, I am assuming you have your application set up in your Google Developer dashboard. That is to say that you have created an application, enabled the necessary YouTube APIs, created a Web Application Client, and set your allowed redirect urls properly.
The flow will work like this:
The user clicks a button (e.g. Add YouTube)
The View calls a method on the Controller to obtain an Authorization URL
On the controller method, we ask Google to give us an Authorization URL based on our client credentials (the ones created in the Google Developer Dashboard) and provide Google with a Redirect URL for our application (this Redirect URL must be in your list of accepted Redirect URLs for your Google Application)
Google gives us back an Authorization URL
We redirect the user to that Authorization URL
User grants our application access
Google gives our application back a special access code using the Redirect URL we provided Google on the request
We use that access code to get the Oauth tokens for the user
We save the Oauth tokens for the user
You need the following NuGet Packages
Google.Apis
Google.Apis.Auth
Google.Apis.Core
Google.apis.YouTube.v3
The Model
public class ExampleModel
{
public bool UserHasYoutubeToken { get; set; }
}
The Controller
public class ExampleController : Controller
{
// I'm assuming you have some sort of service that can read users from and update users to your database
private IUserService userService;
public ExampleController(IUserService userService)
{
this.userService = userService;
}
public async Task<IActionResult> Index()
{
var userId = // Get your user's ID however you get it
// I'm assuming you have some way of knowing if a user has an access token for YouTube or not
var userHasToken = this.userService.UserHasYoutubeToken(userId);
var model = new ExampleModel { UserHasYoutubeToken = userHasToken }
return View(model);
}
// This is a method we'll use to obtain the authorization code flow
private AuthorizationCodeFlow GetGoogleAuthorizationCodeFlow(params string[] scopes)
{
var clientIdPath = #"C:\Path\To\My\client_id.json";
using (var fileStream = new FileStream(clientIdPath, FileMode.Open, FileAccess.Read))
{
var clientSecrets = GoogleClientSecrets.Load(stream).Secrets;
var initializer = new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = clientSecrets, Scopes = scopes };
var googleAuthorizationCodeFlow = new GoogleAuthorizationCodeFlow(initializer);
return googleAuthorizationCodeFlow;
}
}
// This is a route that your View will call (we'll call it using JQuery)
[HttpPost]
public async Task<string> GetAuthorizationUrl()
{
// First, we need to build a redirect url that Google will use to redirect back to the application after the user grants access
var protocol = Request.IsHttps ? "https" : "http";
var redirectUrl = $"{protocol}://{Request.Host}/{Url.Action(nameof(this.GetYoutubeAuthenticationToken)).TrimStart('/')}";
// Next, let's define the scopes we'll be accessing. We are requesting YouTubeForceSsl so we can manage a user's YouTube account.
var scopes = new[] { YouTubeService.Scope.YoutubeForceSsl };
// Now, let's grab the AuthorizationCodeFlow that will generate a unique authorization URL to redirect our user to
var googleAuthorizationCodeFlow = this.GetGoogleAuthorizationCodeFlow(scopes);
var codeRequestUrl = googleAuthorizationCodeFlow.CreateAuthorizationCodeRequest(redirectUrl);
codeRequestUrl.ResponseType = "code";
// Build the url
var authorizationUrl = codeRequestUrl.Build();
// Give it back to our caller for the redirect
return authorizationUrl;
}
public async Task<IActionResult> GetYoutubeAuthenticationToken([FromQuery] string code)
{
if(string.IsNullOrEmpty(code))
{
/*
This means the user canceled and did not grant us access. In this case, there will be a query parameter
on the request URL called 'error' that will have the error message. You can handle this case however.
Here, we'll just not do anything, but you should write code to handle this case however your application
needs to.
*/
}
// The userId is the ID of the user as it relates to YOUR application (NOT their Youtube Id).
// This is the User ID that you assigned them whenever they signed up or however you uniquely identify people using your application
var userId = // Get your user's ID however you do (whether it's on a claim or you have it stored in session or somewhere else)
// We need to build the same redirect url again. Google uses this for validaiton I think...? Not sure what it's used for
// at this stage, I just know we need it :)
var protocol = Request.IsHttps ? "https" : "http";
var redirectUrl = $"{protocol}://{Request.Host}/{Url.Action(nameof(this.GetYoutubeAuthenticationToken)).TrimStart('/')}";
// Now, let's ask Youtube for our OAuth token that will let us do awesome things for the user
var scopes = new[] { YouTubeService.Scope.YoutubeForceSsl };
var googleAuthorizationCodeFlow = this.GetYoutubeAuthorizationCodeFlow(scopes);
var token = await googleAuthorizationCodeFlow.ExchangeCodeForTokenAsync(userId, code, redirectUrl, CancellationToken.None);
// Now, you need to store this token in rlation to your user. So, however you save your user data, just make sure you
// save the token for your user. This is the token you'll use to build up the UserCredentials needed to act on behalf
// of the user.
var tokenJson = JsonConvert.SerializeObject(token);
await this.userService.SaveUserToken(userId, tokenJson);
// Now that we've got access to the user's YouTube account, let's get back
// to our application :)
return RedirectToAction(nameof(this.Index));
}
}
The View
#using YourApplication.Controllers
#model YourApplication.Models.ExampleModel
<div>
#if(Model.UserHasYoutubeToken)
{
<p>YAY! We have access to your YouTube account!</p>
}
else
{
<button id="addYoutube">Add YouTube</button>
}
</div>
<script>
$(document).ready(function () {
var addYoutubeUrl = '#Url.Action(nameof(ExampleController.GetAuthorizationUrl))';
// When the user clicks the 'Add YouTube' button, we'll call the server
// to get the Authorization URL Google built for us, then redirect the
// user to it.
$('#addYoutube').click(function () {
$.post(addYoutubeUrl, function (result) {
if (result) {
window.location.href = result;
}
});
});
});
</script>
As referred here, you need to specify a fix port for the ASP.NET development server like How to fix a port number in asp.NET development server and add this url with the fix port to the allowed urls. Also as stated in this thread, when your browser redirects the user to Google's oAuth page, you should be passing as a parameter the redirect URI you want Google's server to return to with the token response.
I noticed that there is easy non-programmatic way around.
If you have typical monotlith application built in typical MS convention(so not compatible with 12factor and typical DDD) there is an option to tell your Proxy WWW server to rewrite all requests from HTTP to HTTPS so even if you have set up Web App on http://localhost:5000 and then added in Google API url like: http://your.domain.net/sigin-google, it will work perfectly and it is not that bas because it is much safer to set up main WWW to rewrite all to HTTPS.
It is not very good practice I guess however it makes sense and does the job.
I've struggled with this issue for hours in a .net Core application. What finally fixed it for me was, in the Google developers console, to create and use a credential for "Desktop app" instead of a "Web application".
Yeah!! Using credentials of desktop app instead of web app worked for me fine. It took me more than 2 days to figure out this problem. The main problem is that google auth library dose not adding or supporting http://localhost:8000 as redirect uri for web app creds but credentials of desktop app fixed that issue. Cause its supporting http://___ connection instead of https: connection for redirect uri

Recommended way redirect to login page on failed auth when using StatelessAuthenticationConfiguration

I'm using Nancy.Authentication.Stateless 1.4.1 and need to know what is the recommended way of issuing a redirect to a login page whenever the user is not authorized.
Currently, the authentication works in that it returns a 401 to the client. I want to be able to intercept the 401 (server side) and instead send the user to the login page.
I can see that this is possible with Forms Authentication as stated here (https://github.com/NancyFx/Nancy/wiki/Forms-authentication)
Snippet (for Forms Authentication)
var formsAuthConfiguration =
new FormsAuthenticationConfiguration()
{
RedirectUrl = "~/login",
UserMapper = container.Resolve<IUserMapper>(),
};
Just a bit stumped on how to do this when using StatelessAuthenticationConfiguration
You can manually implement this in a custom handler for the After pipeline (see The Application Before After and OnError pipelines) or a module's After hook (see The before and after hooks) that will replace the response with a redirect response it it is unauthorized.
To enable it at the application level, you can use something like this:
pipelines.AfterRequest.AddItemToEndOfPipeline(RedirectUnauthorizedRequests);
where the RedirectUnauthorizedRequests method could look something like this:
private static Action<NancyContext> RedirectUnauthorizedRequests()
{
return context =>
{
if (context.Response.StatusCode == HttpStatusCode.Unauthorized)
{
context.Response = context.GetRedirect("/login");
}
};
}

Categories