Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] OIDC Auth and 401 #446

Closed
Savij opened this issue Jul 3, 2018 · 10 comments
Labels

Comments

@Savij
Copy link

@Savij Savij commented Jul 3, 2018

Hello,
I have the following microservice project scheme:

  • Ocelot - dotnet core 2.0 in webApi template project: port 8090
  • Okta OIDC - dotnet core 2.1 mvc template project: port 8087
  • Micro-Service - dotnet core 2.1 webApi template project: port 8086

(These are all in docker containers)

I'm not sure of how I am supposed to be using this, but here is what I am trying (and failing at):

  1. Using a browser, hit Okta OIDC site (this is an internal corporate Okta instance, not the okta.com site). It redirects me to our SSO login. After login, I am redirected back to the protected route where I can get my access_token (and I'm on a page secured with [Authorize] on the Okta mvc site.

  2. I open up postman and try to hit Ocelot with a path of /test/123. I have this configured to call the micro-service downstream.

  3. I get a 401 Unauthorized.

*Note: If I remove the AuthenticationOptions from the config, Ocelot works fine, it's only when I am trying to get it to use my authentication.

Suspicions

I think what's happening is Okta is setting a browser cookie for it's path which technically is another site because of the ports... Not sure if this is whats happening or not.

What I am trying to accomplish

Please let me know if I am going about this all wrong or not. What I would prefer is that my client-side javacript site (not mentioned here as I thought it might be out of scope) calls out to Okta OIDC to login. Then I would like to take the bearer token and use that in calls to Ocelot. Once Ocelot is happy with that token, I can then setup JWT tokens for trust between Ocelot and my micro-services. (or even better yet, have the web call Ocelot and have it handle the Okta auth).

config

{
    "ReRoutes": [
      {
        "DownstreamPathTemplate": "/service/api/v1/{userId}",
        "DownstreamScheme": "http",
        "DownstreamHostAndPorts": [
          {
            "Host": "salesrep-api",
            "Port": 80
          }
        ],
        "UpstreamPathTemplate": "/test/{userId}",
        "UpstreamHttpMethod": [
          "Get"
        ],
        "AuthenticationOptions": {
          "AuthenticationProviderKey": "ProxyKey",
          "AllowedScopes": []
        }
      }
    ],
    "GlobalConfiguration": {
      "BaseUrl": "http://localhost:8087"
    }
  }

startup (authentication)

 var authenticationProviderKey = "ProxyKey";

            services.AddAuthentication(sharedOptions =>
            {
                sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect(authenticationProviderKey, o =>
            {
                o.ClientId = "xxx";
                o.ClientSecret = "xxx";
                o.Authority = "https://myco.okta.com";
                o.SignInScheme = "Cookies";
                o.CallbackPath = "/auth-response";
                o.ResponseType = OpenIdConnectResponseType.Code;
                o.SaveTokens = true;
                o.UseTokenLifetime = false;
                o.GetClaimsFromUserInfoEndpoint = true;
                o.Scope.Add("openid");
                o.Scope.Add("profile");
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    NameClaimType = "name"
                };
            });
@TomPallister

This comment has been minimized.

Copy link
Member

@TomPallister TomPallister commented Jul 4, 2018

@Savij Thanks for your interest in the project and a very thorough issue report!

On step 2 where you call Ocelot from postman I assume you are setting a header Authorization: Bearer XXXXX?

Have you tried these authentication settings with a normal webapi in docker to remove Ocelot from the equation? You could just create a basic webapi project, stick [Authorize] on the ValuesController and then use your code in the services startup. See if that works called via postman. If it does I would suspect an issue with Ocelot! If it does not work then it is probably an issue where your docker container cannot network to the authority or there is something missing / wrong with the authentication setup code above.

Hope that helps. Let me know and I am happy to keep working to resolve this issue 😄

@Savij

This comment has been minimized.

Copy link
Author

@Savij Savij commented Jul 6, 2018

sounds like a good test. Will try it on Monday when I'm back to work. Will Advise.

@Savij

This comment has been minimized.

Copy link
Author

@Savij Savij commented Jul 10, 2018

@TomPallister I worked on this a bunch yesterday and I was able to get it working. The problem was with Okta and NOT Ocelot. I removed Ocelot as you suggested and it still was not working. I switched over (for now) to implicit grant for the react part. Then added Ocelot and viola!

For those interested, the access_token by default from Okta OIDC is not signed by the same authority as the id_token. For now, I'm using the id_token which has the claims in it I need for this project. I will probably have the IT guys create a separate auth server and change the audience to match. Then I can use the access_token and possibly refresh_tokens.

Here is my final working config if it helps anyone:
[dotnet core c# Startup.cs]

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors();
    var authenticationProviderKey = "ProxyKey";
     services.AddAuthentication(sharedOptions =>
     {
        sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
     .AddJwtBearer(authenticationProviderKey, options =>
    {
        options.Authority = "https://<issuer in okta>.okta.com";
        options.Audience = "<Audience from Okta>";
    });
    services.AddOcelot(Configuration);
    services.AddMvc();
}

public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseCors(builder =>
        builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());

    app.UseStaticFiles();
    app.UseAuthentication();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
    await app.UseOcelot();
}

Then just follow the react implicit grant docs from Okta. Everything I needed was here:
okta react and dotnet core

Only thing I have not done yet is secure the microservices with a JWT token so only they can communicate with Ocelot and each other. Not totally sure I need it as the micro-services will not be reachable by the react app anyway once deployed.... and that's another conversation.
I'm good if you wanna close this ticket.
Thanks for the help!
-Jeff

@TomPallister

This comment has been minimized.

Copy link
Member

@TomPallister TomPallister commented Jul 10, 2018

@Savij awesome thanks for the update. I might put a link to this in the docs in case anyone else is looking for something similar one day.

Let me know if you need anymore assistance with Ocelot 😄

TomPallister pushed a commit that referenced this issue Jul 10, 2018
@SychevIgor

This comment has been minimized.

Copy link
Contributor

@SychevIgor SychevIgor commented Mar 11, 2019

Okta official sample + ocelot integration:
Startup.cs:
ConfigureServices method

        public IConfiguration _ctg { get; }

        public Startup(IConfiguration configuration)
        {
            _ctg = configuration;
        }

services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = OktaDefaults.ApiAuthenticationScheme;
                options.DefaultChallengeScheme = OktaDefaults.ApiAuthenticationScheme;
                options.DefaultSignInScheme = OktaDefaults.ApiAuthenticationScheme;
            })
            .AddOktaWebApi(new OktaWebApiOptions
            {
                OktaDomain = _cfg["Okta:OktaDomain"]
               
            });
services.AddOcelot(_cfg);

and method Configure

app.UseAuthentication();
app.UseOcelot().Wait();

as well as you need nuget package. for example

 <PackageReference Include="Okta.AspNetCore" Version="1.1.4" />
@nitrat7

This comment has been minimized.

Copy link

@nitrat7 nitrat7 commented Jun 17, 2019

@SychevIgor can you provide Sample-Code for Octa-Integration with Okta.AspNetCore?
I'm a little bit lost on reroute-config.

@SychevIgor

This comment has been minimized.

Copy link
Contributor

@SychevIgor SychevIgor commented Jun 18, 2019

@nitrat7 to be fair- almost all code already mentioned.
But regarding reroute configs - you will need 2 extra fields inside AuthenticationOptions :
{
"DownstreamPathTemplate": "/api/xxx",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
],
"UpstreamPathTemplate": "/api/xxx",
"UpstreamHttpMethod": [],
"DangerousAcceptAnyServerCertificateValidator": true,
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": []
}

},

@atadhani

This comment has been minimized.

Copy link

@atadhani atadhani commented Jul 31, 2019

@SychevIgor I am getting an error ArgumentNullException: Your Okta URL is missing. You can copy your domain from the Okta Developer Console. Follow these instructions to find it: https://bit.ly/finding-okta-domain

@SychevIgor

This comment has been minimized.

Copy link
Contributor

@SychevIgor SychevIgor commented Aug 1, 2019

@atadhani a bit confused from message. What do you expect as an answer?
Of course you need configure okta url. In my snippet, application reading okta domain url in a following line:
OktaDomain = _cfg["Okta:OktaDomain"]
If you need more info about .net core configuration https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.2

@atadhani

This comment has been minimized.

Copy link

@atadhani atadhani commented Aug 1, 2019

Sorry for confusion. I was trying _cfg["Okta:OktaDomain"] as _cfg["Okta:"] so it gave me an error when I passed OktaDomain = "" it works now. Thanks for quick response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.