How to use 3Scale's `authrep` function from a server and client perspective? - api

I'm attempting to setup a prototype API using nodejs that uses 3Scale's API management.
I've been able to find their plugin intergration code, which is as follows:
var ThreeScale = require('3scale').Client;
// keep your provider key secret
var client = new ThreeScale("X");
// you will usually obtain app_id and app_key from request params
client.authrep({ app_id: "Y",
app_key: "Z" }, function(response){
if(response.is_success()) {
// continue
} else {
throw new Error("not authorized " + response.error_message);
}
});
Which makes some sense to me as part of a server module. But, I'm not sure where the client's credentials are in that equation....
I see it as the client is pointing to your app, and here's the password for the app...but what about the username/password for the actual client!? where does that get checked?
I feel like I'm not grasping their architecture (possible because it's my first real node project and definitely my first time using 3Scale)...
Further, what's a client's request then look like?

in 3scale system app_id and app_key (in this authentication method) kind of represent the user's (i.e. developer's) credentials. This is due to the fact that every user can have more than one application and one application belongs just to one user, so you don't need user credentials. The credentials are checked on the 3scale system side and if authorized, they report the usage and forward the call to your API.
provider_key identifies your account (API owner) and you have to keep it secret (if someone gets it, they can impersonate you).
Did you already check the 3scale's support site? There are many useful information on the system architecture, some tutorials on integration, etc.
You can check them here: http://support.3scale.net
btw. the node.js plugin is a community plugin. You can also try integration via nginx reverse proxy.

Related

Azure SQL authenticate as user via API with MS Identity Platform

I have an ASP.NET Core 3.1 Web App calling an ASP.NET Core 3.1 Web API, which in turn accesses an Azure SQL database. Authentication is provided via MSAL (Microsoft Identity Platform) - i.e. using the relatively new Microsoft.Identity.Web and Microsoft.Identity.Web.UI libraries.
The goal is to ensure that the user pulls data from SQL via the API under the context of his/her own login, thus enabling row-level security, access auditing and other good things.
I have succeeded in getting the sign-in process to work for the Web App - and through that it obtains a valid access token to access the API using a scope I created when registering the latter with AD.
When I run both the API and the App locally from Visual Studio everything works as expected - the correct access tokens are provided to the App to access the API, and the API to access SQL - in both cases under the user's (i.e. my) identity.
When I publish the API to App Services on Azure, however, and access it there either from a local version of the Web App or an App-Services hosted version of it, the access token that the API gets to access SQL contains the API's Application Identity (system-assigned managed identity), and not the user's identity. Although I can access SQL as the application, it's not what we need.
The Web App obtains its access token using the GetAccessTokenForUserAsync method of ITokenAcquisition - taking as a parameter the single scope I defined for the API.
The API gets its token (to access SQL) like so:
var token = await new AzureServiceTokenProvider().GetAccessTokenAsync("https://database.windows.net", _tenantId)
...where _tenantId is the tenant ID of the subscription.
I have added the SQL Azure Database "user_impersonation" API permission to the AD registration for the API - but that has not helped. As an aside, for some reason Azure gives the full name of this permission as https://sql.azuresynapse.usgovcloudapi.net/user_impersonation - which is slightly alarming as this is just a UK-based regular Azure account.
I have found a few similar posts to this, but mostly for older versions of the solution set. I'm hoping to avoid having to write my own code to post the token requests - this is supposed to be handled by the MSAL libraries.
Should I somehow be separately requesting a SQL access scope from the Web App after sign-in, or should the API be doing something different to get hold of a SQL access token that identifies the user? Why does it work perfectly when running locally?
It seems like this should be a very common use case (the most common?) but it is barely documented - most documentation I've found refers only to the application identity being used or doesn't tell you what to do for this particular tech stack.
Finally - success! In the end this was the critical piece of documentation: Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow - the key points being:
The App only asks for a token to access the API.
The API then requests a token, on behalf of the user identified via the 1st token, to access SQL.
The key is that - since the API cannot trigger a consent window for the second step - I had to use the Enterprise Applications tab in the Azure portal to pre-grant the permissions for SQL.
So the good news is it does work: maybe it's obvious to some but IMO it took me far too long to find the answer to this. I will write up a fuller explanation of how to do this in due course as it can't only be me struggling with this one.
The bad news is that - in the course of my investigations - I found that Azure B2C (which is the next thing I need to add in) doesn't support this "On Behalf Of" flow - click here for details. That's a great shame as I think it's the most obvious use case for it! Oh well, back to the drawing board.
I'm currently working on a similar problem, using a Net5.0 Web app. The reason it appears to be working locally is you are signed into Visual Studio with a user who can access Azure SQL and those are the rights you get in the Db. The IDE is using those credentials in place of the Managed Service Identity, the latter gets used when you upload the app to Azure.
As you noted, in the App registration you need to grant permission to the App for Azure SQL Database user_impersonation.
In your code, you need to request a token from https://database.windows.net//.default (note the // as it's needed for v1 endpoints). By referencing /.default you are asking for all permissions you've selected for the app in the app registration portal.
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#the-default-scope
In Startup.cs you need to EnableTokenAcquisitionToCallDownstreamApi with the scope you require.
services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(new[]
{"https://database.windows.net//.default"})
// Adds the User and App InMemory Token Cache
.AddInMemoryTokenCaches();
services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the
// default policy
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddDbContext<MyDatabaseContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("MyAzureConnection")));
// The database interface
services.AddScoped<ITodos, TodoData>();
services.AddRazorPages()
.AddRazorRuntimeCompilation()
.AddMvcOptions(o =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
o.Filters.Add(new AuthorizeFilter(policy));
})
.AddMicrosoftIdentityUI();
You also need to decorate your controllers with [AuthorizeForScopes(Scopes = new string[]{"https://database.windows.net//.default"}] and include the required scopes for that Controller. For Razor, it's at the top of the page model and requires a reference to `using Microsoft.Identity.Web;'
namespace ToDoApp.Pages.Todos
{
[AuthorizeForScopes(ScopeKeySection = "AzureSQL:BaseUrl")]
public class CreateModel : PageModel
I'm using a section in my appsettings.json for the scope and retrieving it using ScopeKeySection:
"AzureSQL": {
"BaseUrl": "https://database.windows.net//.default",
"Scopes": "user_impersonation"
}
This shows you where to include it for MVC, Razor and Blazor:
https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access#in-mvc-controllers
Finally, your DbContext needs a token which you could pass to it from the client app (perhaps...).
This is how I am doing it at the moment
public class MyDatabaseContext : DbContext
{
private readonly ITokenAcquisition _tokenAcquisition;
public MyDatabaseContext (ITokenAcquisition tokenAcquisition,
DbContextOptions<MyDatabaseContext> options)
: base(options)
{
_tokenAcquisition = tokenAcquisition;
string[] scopes = new[]{"https://database.windows.net//.default"};
var result = _tokenAcquisition.GetAuthenticationResultForUserAsync(scopes)
.GetAwaiter()
.GetResult();
token = result.AccessToken;
var connection = (SqlConnection)Database.GetDbConnection();
connection.AccessToken = result.token;
}
This is a flawed solution. If I restart the app and try to access it again I get an error Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user
It seems to be related to the TokenCache. If I sign out and in again or clear my browser cache the error is resolved. I've a workaround that signs the app in on failure, but it's deficient since I'm using the app's credentials.
However, it successfully connects to the Azure SQL Db as the user instead of the App with the user's rights instead. When I do solve the error (or find one) I will update this answer.

How to track a user is logged in or not using api?

I am creating api using cakePHP. I have created an api for user log in. This log in functionality is working fine.
Here is the log in function -
public function login(){
if ($this->request->is('post')) {
$user = $this->Auth->identify();
}
}
Now, the I am facing problem is, how I can test from other api that is the user is logged in or not? In web application it can be done by default auth system ($this->Auth->user()). But I am not getting how I can check is this user logged in or not from another api. Do I need to send api parameter to each api request ? or any other suggestion ?
Note : I can't send any token in header. Because in header I am sending jwt token. Because in my application there are two kind of authentication. One is log in or not? and another one is depending some other input from user. That is handling by jwt. So jwt token I am already sending by header. So header is already used.
A couple of things to clarify.
In a regular app, the user logs in with a post request and on successful authentication a session is created. This session is a bit of information that the user supplies in each of the following requests and the server then recognises the user. This accomplished by the Auth component in it's default settings.
In an API you could do the same: the user logs in, receives the session and attaches the session cookie-like object on each following requests. (Example of such a Python client.) However, this is not considered good practice as APIs should be stateless (so without requiring something like cookies). The solution of working with tokens, for instance hashes of some secret token together with a timestamp. The Auth component also supports this pretty well. After setting it up, you can simply call $this->Auth->user(), like you would normally and it returns either false or an array of user information. See link below.
Note that by default this authentication type will block unauthenticated users, so you will never see ->user() return false unless you make pages as public.
See also:
(Cookbook > Authentication > Creating stateless authentication systems)

Google Sign-in and Spring Security

I am ashamed to admit that I burned four full days trying to get Spring Security 3.1 to play nicely with Google Sign-in in a standard JSF web application. Both are awesome frameworks in their own right but they seemed incompatible. I finally got it to work in some fashion but strongly suspect that I have missed some fundamental concept and am not doing it the best way.
I am writing an app that our helpdesk uses to track system testing during maintenance activities when our systems are down and cannot host the app, so it is hosted externally. Our Active Directory and IdP are down during this activity so I cannot use our normal authentication systems. Google Sign-in is a perfect solution for this.
Google Sign-in works great in the browser using Google Javascript libraries and some simple code. The browser communicates with Google to determine if the user is already signed in, and if not, opens a separate window where the user can submit credentials and authenticate. Then a small bit of Javascript can send a concise, ephemeral id_token returned from Google to the server which the server can use to verify the authentication independently with Google. That part was easy. The beauty is that if the user is already signed into Gmail or some other Google app, authentication has already happened and Google does not challenge the user again.
Spring Security works great on the server side to protect specified resources and authenticate a user with a username and password. However, in this case, we never see the username or password - the credentials are protected by secure communication between the browser and Google. All we know is whether or not the user is authenticated. We can get the Google username, but Spring Security expects credentials that it can use to authenticate, to a database, in-memory user base, or any other system. It is not, to my knowledge, compatible with another system that simply provides yea-or-nay authentication in the browser.
I found many good examples online that use Spring Boot with EnableOAuth2Sso (e.g. here) but surprisingly few that use Spring Security in a standard app server which does not support EnableOAuth2Sso, and those few did not show any solution I could discern.
Here is how I've done it. I followed Google's simple directions here to provide authentication in the browser. I added this code to the onSignIn() method to send the id_token to the server.
var xhr = new XMLHttpRequest(); // Trigger an authentication for Spring security
xhr.open("POST", "/<my app context>/j_spring_security_check", true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var params = "profileID=" + profile.getId() + "&fullname=" + profile.getName() + "&email=" + profile.getEmail() + "&id_token=" + googleUser.getAuthResponse().id_token
+ "&j_username=" + profile.getEmail() + "&j_password=" + id_token;
xhr.send(params);
window.location.replace("/<my app context>/index.xhtml");
Unfortunately the Spring Authentication object, when passed to the AuthenticationProvider that I provided, did not contain anything but the j_username and j_password parameters as Authentication.getPrincipal() and Authentication.getCredentials(), but this is all I really needed. This is a bit of an abuse of those parameters since I have set them to email and id_token, not username and password.
I wanted to pass the user's full name and email, which Google provides in Javascript as googleUser.getName() and googleUser.getEmail(), to the backend as well. Since Spring Security does not accommodate anything but the username/password, and I was using Primefaces/JSF, I used Primefaces RemoteCommand to call a method on the backing bean with this information. This also feels a little clumsy.
In addition, I had to use window.location.replace() (in code above) because Spring Security did not redirect to my index.xhtml page as expected when I set this in the context with:
<security:form-login login-page='/login.xhtml' authentication-failure-url="/login.xhtml?error=true" default-target-url="/index.html" always-use-default-target="true" />
I have no idea why this does not work.
However, the app does now behave as I want in that it authenticates the user and the authenticated user can access the resources specified in Spring Security, and I wanted to share this in case anyone is doing a similar thing. Can anyone suggest a cleaner/better way? Thanks in advance.

OWIN/OAuth2 3rd party login: Authentication from Client App, Authorization from Web API

I am trying to create a Web API that allows the API's clients (native mobile apps) to login using a 3rd party cloud storage provider. I'm using the following general flow from Microsoft:
Here is what I am trying to achieve:
I am using the default ASP.NET Web API Visual Studio template with external authentication, along with the OWin.Security.Providers Nuget package for Dropbox login functionality, and the existing built-in login functionality for Google (Drive) and Microsoft (OneDrive).
The issue I'm having is that the built-in functionality all seems to do the authentication and authorization as part of one flow. For example, if I set up the following in Startup.Auth.cs:
DropboxAuthenticationOptions dropboxAuthOptions = new DropboxAuthenticationOptions
{
AppKey = _dropboxAppKey,
AppSecret = _dropboxAppSecret
};
app.UseDropboxAuthentication(dropboxAuthOptions);
... and navigate to this url from my web browser:
http://<api_base_url>/api/Account/ExternalLogin?provider=Dropbox&response_type=token&client_id=self&redirect_uri=<api_base_url>
I am successfully redirected to Dropbox to login:
https://www.dropbox.com/1/oauth2/authorize?response_type=code&client_id=<id>&redirect_uri=<redirect_uri>
... and then after I grant access, am redirected back to:
http://<api_base_url>/Help#access_token=<access_token>&token_type=bearer&expires_in=1209600
... as you can see the token is part of that, so could be extracted. The problem is that the client needs to be the one navigating to Dropbox and returning the authorization code back up to the Web API, and the Web API would send the authorization code back to the third party to get the token which would then be returned to the client... as shown in the diagram above. I need the ExternalLogin action in the AccountController to somehow retrieve the Dropbox url and return that to the client (it would just be a json response), but I don't see a way to retrieve that (it just returns a ChallengeResult, and the actual Dropbox url is buried somewhere). Also, I think I need a way to separately request the token from the third party based on the authorization code.
This post seems a little similar to what I am trying to do:
Registering Web API 2 external logins from multiple API clients with OWIN Identity
... but the solution there seems to require the client to be an MVC application, which is not necessarily the case for me. I want to keep this as simple as possible on the client side, follow the flow from my diagram above, but also not reinvent the wheel (reuse as much as possible of what already exists in the OWIN/OAuth2 implementation). Ideally I don't want the client to have to reference any of the OWIN/OAuth libraries since all I really need the client to do is access an external url provided by the API (Dropbox in my example), have the user input their credentials and give permission, and send the resulting authorization code back up to the api.
Conceptually this doesn't sound that hard but I have no idea how to implement it and still use as much of the existing OAuth code as possible. Please help!
To be clear, the sample I mentioned in the link you posted CAN be used with any OAuth2 client, using any supported flow (implicit, code or custom). When communicating with your own authorization server, you can of course use the implicit flow if you want to use JS or mobile apps: you just have to build an authorization request using response_type=token and extract the access token from the URI fragment on the JS side.
http://localhost:55985/connect/authorize?client_id=myClient&redirect_uri=http%3a%2f%2flocalhost%3a56854%2f&response_type=token
For reference, here's the sample: https://github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server
In case you'd prefer a simpler approach (that would involve no custom OAuth2 authorization server), here's another option using the OAuth2 bearer authentication middleware and implementing a custom IAuthenticationTokenProvider to manually validate the opaque token issued by Dropbox. Unlike the mentioned sample (that acts like an authorization proxy server between Dropbox and the MVC client app), the JS app is directly registered with Dropbox.
You'll have to make a request against the Dropbox profile endpoint (https://api.dropbox.com/1/account/info) with the received token to validate it and build an adequate ClaimsIdentity instance for each request received by your API. Here's a sample (but please don't use it as-is, it hasn't been tested):
public sealed class DropboxAccessTokenProvider : AuthenticationTokenProvider {
public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context) {
using (var client = new HttpClient()) {
var request = new HttpRequestMessage(HttpMethod.Get, "https://api.dropbox.com/1/account/info");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.Token);
var response = await client.SendAsync(request);
if (response.StatusCode != HttpStatusCode.OK) {
return;
}
var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
var identity = new ClaimsIdentity("Dropbox");
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, payload.Value<string>("uid")));
context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
}
}
}
You can easily plug it via the AccessTokenProvider property:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
AccessTokenProvider = new DropboxAccessTokenProvider()
});
It has its own downsides: it requires caching to avoid flooding the Dropbox endpoint and is not the right way to go if you want to accept tokens issued by different providers (e.g Dropbox, Microsoft, Google, Facebook).
Not to mention that if offers a very low security level: since you can't verify the audience of the access token (i.e the party the token was issued to), you can't ensure that the access token was issued to a client application you fully trust, which allows any third party developer to use his own Dropbox tokens with your API without having to request user's consent.
This is - obviously - a major security concern and that's why you SHOULD prefer the approach used in the linked sample. You can read more about confused deputy attacks on this thread: https://stackoverflow.com/a/17439317/542757.
Good luck, and don't hesitate if you still need help.

Problems working with Google Calendar Api V3 and PHP

I'm just trying to make a little, simple application (that i already made two years ago in Objective-C with api V1) that presents a screen with time of event and description and a button : "insert event in your calendar".
Every user has, obviously, to configure the application with his google username and password.
The app simplifies some process using the first calendar available.
I had infinite problem trying to do it with javascript (this app will be made in html5), so, looking at docs, I ended up trying to make a back-end on my server in php5 (thought it could be easier...ohohoho).
So, i read docs from here : https://developers.google.com/google-apps/calendar/
What i did :
1)
Get to the Google Developers Console.
Created a project.
I now have this (not real keys):
OAuth 2.0
Client ID 352xxxyy9.apps.googleusercontent.com
Email address 3527xxxy#developer.gserviceaccount.com
Service Account
Client ID 3523xxxyy419-vpfgdfg9u77s0.apps.googleusercontent.com
Email address 35ssss9-zzzzsnhavna78ea0b9gvn6a9u77s0#developer.gserviceaccount.com
Public key fingerprints :ac15ddfxdffrtg5565fgfg545r
2)
I installed Google APIs Client Library for PHP (beta) in my server.
doc says:
Using the Google APIs Client Library for PHP requires that you download the PHP source. In the future, packages will be provided. Refer to the project page for more details.
Run the following commands to download and install the source: svn blaj blah blah.
I copied the entire source in my server. Easy :)
Then..
3) You can now import the classes you will need using the following statements:
require_once "../src/apiClient.php";
require_once "../src/contrib/apiCalendarService.php";
Ok, i'll insert them in my php script !
4)" Configure your app"
You must instantiate a client to make requests to the API. All requests to the Google Calendar API require authorization.
The following code demonstrates how to configure an authorized service object using OAuth 2.0 for native applications. For more information, see Authorize Requests.
To find your project's client ID and client secret, do the following:
Go to the Google Developers Console.
Select a project.
In the sidebar on the left, select APIs & auth. In the displayed list of APIs, make sure the Google Calendar API status is set to ON.
In the sidebar on the left, select Credentials.
Find the lines labeled Client ID and Client secret. Note that there may be a client ID without a client secret, for use with Compute Engine and App Engine; in that case, create a new client ID and client secret by selecting Create New Client ID.
Edit the src/config.php file to put in your developer API information.
global $apiConfig;
$apiConfig = array(
// Site name to show in Google's OAuth authentication screen
'site_name' => 'www.example.org',
// OAuth2 Setting, you can get these keys in Google Developers Console
'oauth2_client_id' => 'YOUR_CLIENT_ID',
'oauth2_client_secret' => 'YOUR_CLIENT_SECRET',
'oauth2_redirect_uri' => 'YOUR_REDIRECT_URL',
// The developer key; you get this from Google Developers Console
'developer_key' => 'YOUR_DEVELOPER_KEY',
...
// Which Authentication, Storage and HTTP IO classes to use.
'authClass' => 'apiOAuth2',
....
// Definition of service specific values like scopes, OAuth token URLs, etc
'services' => array(
'calendar' => array('scope' => 'https://www.googleapis.com/auth/calendar'),
)
);
But they are DIFFERENT from the key i have, what's wrong ????
What are client secrets ? redirect_url??
Please help.
I think you need to setup a service account access as described here:
https://code.google.com/p/google-api-php-client/wiki/OAuth2#Service_Accounts
I had difficulties to get it work as I made many trial and errors and my cache got filled with non-working token.
If ever you find yourself not able to access the calendar even after following all the steps, try to change this line of code:
$client->setAssertionCredentials(new Google_AssertionCredentials(SERVICE_ACCOUNT_NAME, array('https://www.googleapis.com/auth/prediction'),$key));
to this:
$client->setAssertionCredentials(new Google_AssertionCredentials(SERVICE_ACCOUNT_NAME, array('https://www.googleapis.com/auth/prediction'),$key, 'notasecret','http://oauth.net/grant_type/jwt/1.0/bearer',false,false));
The last false tells AssertionCredential class to not use any cache. I did it once and then it worked with it set to true afterward.
First go here https://console.developers.google.com/project that is where you configure your app...
Click on your project, then on the left side you will see APIs & Auth, click on Credentials. You will need to create your OAuth, and Public API Access keys.
Once you have done that you will then enter those into the appropriate client_id, secret, redirect etc.
The redirect uri is the same page your app is on, its the page the user gets sent back to after authorizing.
I had the same problem.
On this page, when you click on the Create new Client Id, choose Web application and it shoudl give you the client secret key as well.
https://console.developers.google.com/project
Add a project etc.
Hope it helps