How can I authenticate using Azure AD? Am I understanding the process correctly? #1781
Replies: 10 comments
-
Typically with an API gateway you wouldn't do interactive authentication by redirecting the user to a login page. This should be handled by the client instead who will redirect the user to the Azure AD login page and get a token back. You can then use that token to access your API gateway which can then validate the token, usually by validating the signature. If the token isn't valid it will return a 401 Unauthorized. This isn't really different from a regular API to be honest, but the value of doing it through a gateway is that you've centralised this concern in a single place. Does that answer your question? |
Beta Was this translation helpful? Give feedback.
-
Yes I think so, |
Beta Was this translation helpful? Give feedback.
-
I'm attempting to use the azure integration in ocelot i.e the AzureADJwtBearer scheme. This called the AuthenticateAsync method from the Http Context, so what is the purpose of this method? I'm using the Microsoft Identity library. |
Beta Was this translation helpful? Give feedback.
-
Yes, you would indeed make a registration for your API gateway in Azure AD and then have the client request a token from Azure AD and have it send that along for requests to your gateway. It will validate the token and depending on whether it is valid allow access to whatever backend API's you expose through your gateway. You can use scopes to control if the user has access to all API's exposed through your gateway or only a subset of them. I'm not entirely sure what you mean with your last comment. Are you receiving an error or is something happening that you're not expecting? Could you share your Program.cs, Startup.cs and your Ocelot configuration? That would help me figure out what's going on. |
Beta Was this translation helpful? Give feedback.
-
Sure! public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddProtectedWebApi(Configuration).AddProtectedApiCallsWebApis(Configuration).AddInMemoryTokenCaches();
services.AddOcelot(Configuration);
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseOcelot().Wait();
app.UseAuthentication();
}
} Program.cs public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureAppConfiguration((host, config) =>
{
config.AddJsonFile("ocelot.json");
})
.UseStartup<Startup>();
});
} ocelot.json {
"Routes": [
{
"DownstreamPathTemplate": "/Test1/Get",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/test1/Get",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/Test2/Get",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5002
}
],
"UpstreamPathTemplate": "/test2/Get",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/Admin/GetWRPExceptions",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5003
}
],
"UpstreamPathTemplate": "/wrp/user",
"UpstreamHttpMethod": [ "GET" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "AzureADJwtBearer",
"AllowedScopes": []
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000",
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
} |
Beta Was this translation helpful? Give feedback.
-
I just tried a few things out and I think I understand better. What helps me understand is to think of ocelots authentication as simply doing token validation, so whatever client ID I provide ocelot is the ID that is being validated against. For example, I set up my gateway to validate against one of the APIs inside my system (lets say client id A). So in this case, I can set the authorize tag on my API A and ocelot will take the bearer token, validate it, then pass it to API A which will then execute my request. IF however, I change the client ID in ocelot to client ID B, even if I pass the same bearer token, this time I will get a 401 error becuase it fails Ocelots authentication. This is why I need to register Ocelot its own azure app so I can put multiple APIs behind it. I have this working now, thank you for the responses, they helped a ton! |
Beta Was this translation helpful? Give feedback.
-
No worries, I'm glad I could help out. Good luck building your API gateway. |
Beta Was this translation helpful? Give feedback.
-
thanks for all the code for the ocelot items. Could you guys help me understand how I can grab a token through post man and then submit a request through authorization headers to the web service to test them out? I am currently stuck at that part |
Beta Was this translation helpful? Give feedback.
-
Ok this is what i learned, currently as of Microsoft.Identity.Web 0.4.0-preview, the code is looking for roles, so you need to make sure you App Reg has a role or it will error out. the other way to do this is not use Microsoft.Identity.Web. You can just authenticate regularly. You have to change the AuthenticationProviderKey (token name), see ocelot.json at bottom. Added to startup.cs: public void ConfigureServices(IServiceCollection services)
{
// Azure AD
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, jwtOptions => {
jwtOptions.Audience = "{C L I E N T I D}";
jwtOptions.Authority = "https://login.microsoftonline.com/{D O M A I N}";
jwtOptions.RequireHttpsMetadata = false;
jwtOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed,
OnTokenValidated = AuthenticationTokenValidated
};
jwtOptions.TokenValidationParameters.ValidIssuer = "https://login.microsoftonline.com/{T E N I N E T I D}/v2.0";
});
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, jwtOptions => {
jwtOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed,
OnTokenValidated = AuthenticationTokenValidated
};
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
//.AddMicrosoftIdentityWebApi(Configuration,"AzureAD");
//services.AddProtectedWebApi(Configuration).AddProtectedApiCallsWebApis(Configuration).AddInMemoryTokenCaches();
// Call Ocelot
services.AddOcelot(Configuration);
services.AddControllers();
} To get the token to pass back through postman, you need to make sure you pass the identity token not the access token. You can get that by sending in a request. I am using a python script to generate it, but there are prob better ways as well. (https://github.com/Azure-Samples/ms-identity-python-webapp#step-2-register-the-sample-with-your-azure-active-directory-tenant) link to project, added a print to the graph route like so: @app.route("/graphcall")
def graphcall():
token = _get_token_from_cache(app_config.SCOPE)
#print(token)
import json
print(json.dumps(token)) take the console dump, and pull out the identity token You can add these two to the bottom of the startup.cs to help debug: private Task AuthenticationFailed(AuthenticationFailedContext arg)
{
// For debugging purposes only!
var s = $"AuthenticationFailed: {arg.Exception.Message}";
arg.Response.ContentLength = s.Length;
//uncomment this out to view error on token
//arg.Response.Body.Write(System.Text.Encoding.UTF8.GetBytes(s), 0, s.Length);
return Task.FromResult(0);
}
private Task AuthenticationTokenValidated(TokenValidatedContext arg)
{
// For debugging purposes only!
var s = $"AuthenticationTokenValidated: {arg.Result}";
return Task.FromResult(0);
} Hope this helps anyone looking into this. Also if anyone is looking into rate limited here is a sample from my ocelot.json: {
"Routes": [
{
"DownstreamPathTemplate": "/weatherforecast",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44339
}
],
"UpstreamPathTemplate": "/api/weather",
"UpstreamHttpMethod": [ "Get" ],
"AuthenticationOptions": {
//"AuthenticationProviderKey": "AzureADJwtBearer",
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": []
},
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "5s",
"PeriodTimespan": 1,
"Limit": 1
},
"FileCacheOptions": { "TtlSeconds": 30 }
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:5021",
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
} |
Beta Was this translation helpful? Give feedback.
-
Is it possible to use AllowedScopes when using AAD Client Credentials grant flow? My authentication is working but I am unable to configure Scopes. In a Client Credentials grants flow, AppRoles are returned in token. Can Ocelot use these and lookup on configured AllowedScopes for authorization? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I am trying to authenticate using my Ocelot gateway via azure active directory. All of the downstream APIs have azure authentication, so I was hoping to have all users route through the gateway, the gateway would issue an authorization request (at which point the user enters username and password) and then stores the resulting bearer token for the requested resource. I have included a link to my question in more detail on stack overflow:
Everything seems to be set up correctly, which leads me to believe I am misunderstanding how this should work.
Beta Was this translation helpful? Give feedback.
All reactions