Using ambassador authservice to only require basic auth on some routes/urls (or services) - authentication

I want to activate ambassador authservice to only require authentication on certain routes/urls. Now if you install the basic http auth service it requires this auth for all services by default. So how can I configure ambassador or the auth service (separate service with ExAuth) to only require auth on certain routes/urls?
Ambassador version 0.51.2
kubernetes version 1.14
auth service I am using as base: https://github.com/datawire/ambassador-auth-httpbasic

If you see the server.js example in https://github.com/datawire/ambassador-auth-httpbasic you'll see that it's only authenticating for /extauth/qotm/quote*. If you are using the same server.js to start you'll have to add another app.all section with whatever you want to authenticate. For example:
app.all('/extauth/myapp/myurl*', authenticate, function (req, res) {
var session = req.headers['x-myapp-session']
if (!session) {
console.log(`creating x-myapp-session: ${req.id}`)
session = req.id
res.set('x-myapp-session', session)
}
console.log(`allowing MyApp request, session ${session}`)
res.send('OK (authenticated)')
})
Or you can implement this server using a different language if you'd like too.

Related

Looking for a better way to authenticate Google Cloud Function with a service account. Right now I'm storing the credentials json file on the backend

I'm looking for a better way to authenticate Google Cloud Function with a service account. Right now I'm storing the credentials json file on the backend. This is the code for my app https://github.com/ChristianOConnor/spheron-react-api-stack. This app could be deployed on any hosting platform, but at the moment the app is built to deploy on a Web3 protocol called Spheron. TLDR, Spheron runs the backend express server on a web3 friendly content serving/hosting platform called Akash. This means that whoever is hosting my backend express server has access to my GCP service account's credentials. You can see all of the code in the link I provided but just for ease of access this is the server.js file which will be on Akash.
server.js
var express = require("express");
var app = express();
require("dotenv").config();
const GoogleAuth = require("google-auth-library").GoogleAuth;
const cors = require("cors");
app.use(
cors({ origin: process.env.ORIGIN, credentials: process.env.CREDENTIALS })
);
app.get("/hello", async function (req, res) {
const keyInJsn = JSON.parse(process.env.CREDENTIALS_STR);
const auth = new GoogleAuth({
credentials: keyInJsn,
});
const url = process.env.RUN_APP_URL;
//Create your client with an Identity token.
const client = await auth.getIdTokenClient(url);
const result = await client.request({ url });
const resData = result.data;
res.send(resData);
});
var server = app.listen(8081, function () {
var host = server.address().address;
var port = server.address().port;
console.log("Example app listening at http://localhost:", port);
});
process.env.CREDENTIALS_STR is the service account credentials set up in this format:
CREDENTIALS_STR={"type": "service_account","project_id": "<PROJECT ID>","private_key_id": "<PRIVATE KEY ID>","private_key": "-----BEGIN PRIVATE KEY-----\<PRIVATE KEY>\n-----END PRIVATE KEY-----\n","client_email": "<SERVICE ACCOUNT NAME>#<PROJECT NAME>.iam.gserviceaccount.com","client_id": "<CLIENT ID>","auth_uri": "https://accounts.google.com/o/oauth2/auth","token_uri": "https://oauth2.googleapis.com/token","auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/<SERVICE ACCOUNT NAME>.iam.gserviceaccount.com"}
The Akash provider can see this string. Is there a better way to do authentication for a GCP service account that doesn't expose the credntials to a hosting/server provider?
Also don't be throw off by the web3 stuff. This app essentially works the same as a traditional web2 app with a backend and a client. If it helps you to think about it different, picture that I'm deploying on Netlify with a static client and a Netlify Function.
The compromise I came to was creating an API Gateway for the function. This allows the function to be called without any credentials and still run from a service account. It creates a separate quasi-vulnerability though, as anyone with the API Gateway link can also call the function unauthenticated.
First, I enabled Service Management APIs, API Gateway API, and Service Control API. Then I made an API Gateway with my service account that runs my referenced cloud function. I uploaded a file like this for the api spec:
swagger: '2.0'
info:
title: api-gateway-cloud-function
description: API Gateway Calling Cloud Function
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/whateveryouwanttocallthispath:
get:
summary: My Cloud Function
operationId: whatever
x-google-backend:
address: <CLOUD_RUN_URL>
responses:
'200':
description: OK
You can test it by running the function via curl command in a bash terminal curl {gatewayId}-{hash}.{region_code}.gateway.dev/v1/whateveryouwanttocallthispath. It works with no credential json file.
The problem is that you could achieve a similar result by just allowing the function to be called unauthenticated... Idk if this method has many benefits.

How to validate a token that is send by socket.io using passport ? I am using passport-azure-ad strategy

I have an application that is using passport-azure-ad strategy to authenticate users. When the client sends a post or get request, I have a middleware that checks if the request is valid or not with passport.authenticate('oath-bearer', {session: false})(requires, next) and this work perfectly Fine.
But, on this same application, I am also using socket.io for uploading images. When the client tries to establish socket connection with the server, it sends a token on the header like this - `const socket = io('http://localhost:3000', auth: {token : 'eyhadjhad...'})`. I have access to this token on the server side like this - const token = socket.handshake.auth.token . Now I am having trouble authenticating this token.
Is there a way I can add a middleware on namespaces like the one I have for routes? for example like this -
io.of('/fileUpload')
.use((socket, next) =>
passport.authenticate(token)
)).on('connection', (socket) => {
console.log('user authenticated, allow upload')
})

OpenIddict support returning authorization code via GET request for postman

I have set up an Authorization Server using OpenIddict 3.1.1 (porting over an existing one that was using the older ASOS package directly). I believe I am most of the way there, because when using the client application, I am able to log in, give consent, redirect back to the client, and exchange the authorization code for an access token.
However, when I try to do the same using Postman's OAuth 2.0 authentication support, I am able to log in (and give consent), but when it completes and returns the authorization code, I receive an HTTP 403 from the https://oauth.pstmn.io/v1/callback that I am redirected to:
403 ERROR
The request could not be satisfied.
This distribution is not configured to allow the HTTP request method that was used for this request. The distribution supports only cachable requests. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront)
Request ID: UAXpago6ISiqbgm9U_SVPwh96qz1qoveZWFd0Cra-2FximeWZiY2aQ==
From what I can tell, this is because OpenIddict is issuing a POST request back to the callback url. This works for my client application, but evidently is not supported by Postman.
What configuration tweak do I need to make to OpenIddict to support this in postman?
OpenIddict related config in Startup.ConfigureServices:
services.AddOpenIddict()
.AddCore(options => {
options.AddApplicationStore<ClientStore>();
options.UseEntityFramework()
.UseDbContext<OAuthServerDbContext>()
.ReplaceDefaultEntities<Client, Authorization, OAuthScope, Token, long>()
;
})
.AddServer(options => {
options.RegisterClaims();
options.RegisterScopes(OpenIddictConstants.Scopes.OpenId,
OpenIddictConstants.Scopes.Email,
OpenIddictConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Profile,
"user");
// flows
options.AllowAuthorizationCodeFlow();
options.AllowRefreshTokenFlow();
options.AllowPasswordFlow();
options.AllowHybridFlow();
// implicit is used by postman
options.AllowImplicitFlow();
var serviceProvider = options.Services.BuildServiceProvider();
var oauthConstants = serviceProvider.GetRequiredService<IOptions<OAuthConstants>>().Value;
var tokenLifetimes = serviceProvider
.GetRequiredService<IOptions<OpenIdConnectServerTokenLifetimeSettings>>().Value;
// security
options.SetAccessTokenLifetime(tokenLifetimes.AccessTokenLifetime)
.SetAuthorizationCodeLifetime(tokenLifetimes.AuthorizationCodeLifetime)
.SetIdentityTokenLifetime(tokenLifetimes.IdentityTokenLifetime)
.SetRefreshTokenLifetime(tokenLifetimes.RefreshTokenLifetime);
options.SetIssuer(new Uri("https://localhost/oauth/"));
// custom handlers added here
options.AddEventHandlers();
// certificate details hidden
options.AddEncryptionCertificate(certificate);
// endpoints
options.SetAuthorizationEndpointUris("/OpenIdConnect/Authorize");
options.SetLogoutEndpointUris("/OpenIdConnect/Logout", "/Account/Logout");
options.SetRevocationEndpointUris("/OpenIdConnect/Revoke");
options.SetTokenEndpointUris("/OpenIdConnect/Token");
options.SetCryptographyEndpointUris("/OpenIdConnect/JWKDoc");
options.SetUserinfoEndpointUris("/OpenIdConnect/UserInfo");
options.UseAspNetCore()
.EnableStatusCodePagesIntegration()
.EnableAuthorizationEndpointPassthrough()
//.EnableTokenEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
;
})
.AddValidation(options => {
options.UseLocalServer();
options.UseAspNetCore();
var serviceProvider = options.Services.BuildServiceProvider();
var config = serviceProvider.GetRequiredService<IConfiguration>();
options.SetClientId(config.GetValue<string>(nameof(Settings.OAuthClientId)));
options.SetClientSecret(config.GetValue<string>(nameof(Settings.ClientSecret)));
// certificate details hidden
options.AddEncryptionCertificate(certificate);
});
Postman details:
Authorization
Token Name: Redacted
Grant Type: Authorization Code
Callback URL: disabled, https://oauth.pstmn.io/v1/callback
Authorize using browser: checked
Auth URL: https://localhost/oauth/OpenIdConnect/Authorize
Access Token URL: https://localhost/oauth/OpenIdConnect/Token
Client ID: redacted, but correct
Client Secret: redacted, but correct
Scope: openid offline_access
State:
Client Authentication: Send client credentials in body
edit: The response that it sends to the postman callback URI does include the authorization code in the body, but because of the 403 response, Postman doesn't parse that out and make the follow-up request to exchange the code for the token.
There is an option that you can set to control if the authorization code is received in the URL as a query string or in the body as a post. The option is response_mode and you control that as a client.
I believe if it is not set to response_mode=form_post, then you will get the code in the URL instead.
See the details about this parameter here.

How to integrate HTTP Digest Auth into Strongloop's Loopback?

I'm relatively new to Strongloop's Loopback.
A project I'm working on requires HTTP-Digest to use as authentication.
I have setup the ACL on the models (and endpoints). SPA client uses REST to consume services.
I'm stuck on how to use http digest auth (username:realm:password) / nonce instead of the plain login of username/password.
I still would like to use the token auth also.
I'm currently looking at the ff 3 projects though:
loopback-component-auth
passport-http
loopback-component-passport
Any help would be appreciated! Thank you!
You can use Express Middleware to configure HTTP authentication:
Use this node module: http-auth
Create digest-auth.js boot script in server/boot folder
var auth = require('http-auth');
var basic = auth.basic({
realm: "<your authentication realm>",
file: __dirname + "<path to your .htpasswd file"
});
module.exports = function (app) {
app.use(auth.connect(basic));
// Setup route.
app.get("/", (req, res) => {
res.send("Secured resource access granted!");
});
}
You can check more option available with "http-auth" module to use "username:realm:password" for authentication
Hope this would help you !

Multiple auth schemes in hapijs?

I am building an application using hapi.js . The clients of this application are going to be either a web application, so authentication is via JWT in the coookie or via OAuth2 clients which are going to be sending the Bearer key header.
Is there some way that the framework allows using both schemes for the same route? I want the authentication to fail if both schemes fail, but pass if either of the go through.
Look at http://hapijs.com/api#route-options under auth.strategies. This will allow you to set multiple strategies for your route. You can define the behaviour with auth.mode.
hapi supports multiple authentication strategies for a route. Register the indiviual plugins for authentication and set the default auth scheme afterwards.
var Hapi = require('hapi')
var BasicAuth = require('hapi-auth-basic')
var CookieAuth = require('hapi-auth-cookie')
// create new server instance
var server = new Hapi.Server()
// register plugins to server instance
server.register([ BasicAuth, CookieAuth ], function (err) {
if (err) {…}
server.auth.strategy('simple', 'basic', { validateFunc: basicValidationFn })
server.auth.strategy('session', 'cookie', { password: '…' })
server.auth.default('simple')
})
Each authentication scheme may require dedicated configuration (like a cookie password, a validation function, etc.) that you need to provide.