Need assistance with custom authentication in Istio/kubernates - node.js

I am new to Istio and I have learned a lot and applied to my project which consist of many Microservices. I am stuck in Authentication when it comes to using Istio
So the issue is this. Istio offers authentication which involves using Oauth google, Oauth or any other provider. and Once we do this, we can setup AuthPolicy and define which microservices we want it to apply to. I have attached my auth policy yaml and it works fine. Now may project at Job requires me to use custom auth also. In other words, I have one microservice which handles authentication. This auth microservice has three end points /login ,/singup, /logout and /auth. Normally, In my application, I would call /auth as a middleware to before I make any other call to make sure the user is logged in. /auth in my microservice reads jwt token I stored in a cookie when I logged in at a first place and check if it is valid. Now my question is how to add my custom authentication rather than using Oauth?. Now as you know auth policy.yaml I attached will trigger auth check at sidecar proxy level; so I don't need to direct my traffic to ingress gateway; that means my gateway takes care of mtls while sidecar takes care of jwt auth check. So how to plug in my custom auth in policy.yaml or another way such that "I don't need to redirect my all traffic to ingress gateway".
In short please help me with how to add my custom auth jwt check-in policy.yaml like in the picture or any other way and if required modify my auth [micro-service][1] code too. People suggest redirecting traffic to ingress gateway and add envoy filter code there which will redirect traffic to auth microservices. But I don't have to redirect my all calls to ingress gateway and run envoy filter there. I want to achieve what istio already doing by defining policy yaml and jwt auth check happens at sidecar proxy level suing policy.yaml; so we don't redirect traffic to ingress gateway.
Np: my all microservices are in ClusterIP and only my front end is exposed outside
Looking forward to your help/advice
Heres my code for auth policy.yaml
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: reshub
spec:
targets:
- name: hotelservice // auth check when ever call is made to this microservice
peers:
- mtls: {}
origins:
- jwt:
issuer: "https://rshub.auth0.com/"
jwksUri: "https://rshub.auth0.com/.well-known/jwks.json"
principalBinding: USE_ORIGIN
here's my code for auth microservice just to show you my current log of checking jwt
#app.route('/auth/varifyLoggedInUser',methods=['POST'])
def varifyLoggedInUser():
isAuthenticated = False
users = mongo.db.users
c = request.cookies.get('token')
token = request.get_json()['cookie']
print(token)
if token:
decoded_token = decode_token(token)
user_identity =decoded_token['identity']['email']
user = users.find_one({'email': user_identity,'token':token})
if user:
isAuthenticated = True
return jsonify({'isAuthenticated' : isAuthenticated,'token':c})

Try the AuthService project here which seems to aim to improve this area of Istio, which is at the moment pretty deficient IMO:
https://github.com/istio-ecosystem/authservice
I think the Istio docs imply that it supports more than it really does - Istio will accept and validate JWT tokens for authorization but it provides nothing in the way of authentication.

Related

How can I configure Identity Server to correctly validate JWT tokens after an Azure App Slot Switch?

I have a core hosted API which uses IdentityServer and issues JwtBearer tokens to my desktop based client. This is generally working and I can log in and use the application as expected.
However, when using Azure Deployment Slots I run into problems due to the Issuer validation.
When swapping slots, azure doesn't swap the running code, but rather just swaps the pointing urls so that a warmed up running app is ready to serve requests immediately. However, the Identity Server implemenation seems to keep a reference to the OLD slot URL and use this as part of the Issuer Validation.
This means that once the slots are swapped, not only are all the clients effectively logged out (which is bad enough), but then when the client logs in again the token that the Identity Server supplies isn't even valid becuase it's got the wrong URI for the issuer.
This results in the error:
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:
IdentityServerJwtBearer was not authenticated. Failure message:
IDX10205: Issuer validation failed. Issuer:
'https://my-app-name.azurewebsites.net'. Did not match:
validationParameters.ValidIssuer:
'https://my-app-name-deployment.azurewebsites.net' or
validationParameters.ValidIssuers: 'null'.
I have tried to disable Issuer Validation by doing this in the setup:
services.AddAuthentication(options =>
{
})
.AddCookie()
.AddJwtBearer(options =>
{
options.TokenValidationParameters.ValidateIssuer = false;
})
.AddIdentityServerJwt();
However this doesn't seem to make any difference. Am I setting this variable in the wrong place? Are there other settings that need to be also configured as well as this to bypass this check?
I have also tried setting the list of ValidIssuers to include both 'https://my-app-name.azurewebsites.net' and 'https://my-app-name-deployment.azurewebsites.net' in the hopes that either one would be accepted and allow tokens to be validated by either slot, but again this seems to make no difference.
Alternatively, Is there a way to pervent IdentityServer caching the Issuer Url - or a way to flush that cache without restarting the application? Currently once the slots are swapped the only way I can get the desktop application to access the API is to restart the API application and then log in again aftwards (just logging out and logging in still results in a token that the server cannot validate, even though the server just issued it).
I feel like I must be missing something glaringly obvious, but I can't see what it is...
To configure the IdentityServer JWT Bearer you can use a configure call:
services.Configure<JwtBearerOptions>(IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.TokenValidationParameters.ValidateIssuer = false;
});
This way it the ValidateIssuer flag is set on the IdentityServerJwt rather than the original code which set up a new JwtBearer which was entirely seperate from the IdentityServer one.
Once the configuration is being set on the correct service it is also possible to use the array of ValidIssuers to include the Asure Slot Urls that should be accepted.

Getting IDX10205 when accessing backend api from frontend

I'm trying to set up a frontend React app service and a backend Node app service, which both require authentication, according to this tutorial.
I've followed the steps except that I needed to set "loginParameters": ["response_type=code id_token", "scope=openid api://<backend app id>/user_impersonation"] instead of additionalLoginParams since my app uses auth v2.
When my frontend app calls my backend api I get the following error
{"code":401,"message":"IDX10205: Issuer validation failed. Issuer: '[PII is hidden]'. Did not match: validationParameters.ValidIssuer: '[PII is hidden]' or validationParameters.ValidIssuers: '[PII is hidden]'."}
I don't know how to debug this as the useful information in the response is hidded and I can't find a way to show it when using Node. I have inspected the token and the issuer is https://sts.windows.net/<tenant id>/, but I don't know what's expected or how to set ValidIssuer.
What I do for authentication code-wise is calling /.auth/me from frontend after login to receive an access token and this token is passed to the backend api in the header as Authentication: Bearer <access_token>. I'm expecting Azure to handle everything else according to the settings made in the linked tutorial. Is this correct?
How can I debug this issue?
EDIT
This is how the Expose an API page of backend app registration looks.
This is the data of my access token.
Your question has been resolved, post it as the answer to the end of the question.
As I said in the comments, you need to obtain the 2.0 version of the token. So you need to change the accessTokenAcceptedVersion attribute of the application manifest to: "accessTokenAcceptedVersion": 2.

"authentication failed due to: jwt audience is invalid" with Azure AD

First I'm describing how I setup my applications then I will describe how I'm using the APIs.
Setup
In my Azure Active Directory, I have two applications registered: UI and Backend
UI has the client ID clientId1 and backend has client ID clientId2 (it's a GUID, but for simplicity)
Both are under the same tenant tentant1 (single tenant)
Backend (Web API)
Backend has an exposed API with scope "api://clientId2/access_as_user" and authorized client "clientId1" with the scope just mentioned selected
I'm using passport and passport-azure-ad (I pretty much copied https://github.com/Azure-Samples/active-directory-javascript-nodejs-webapi-v2).
My config:
const config = {
identityMetadata: "https://login.microsoftonline.com/tenant1/v2.0/.well-known/openid-configuration",
clientID: "clientId2",
validateIssuer: false,
loggingLevel: 'info',
passReqToCallback: false,
loggingNoPII: false
};
I get this message when starting the server:
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":16052,"level":40,"msg":"Production environments should always validate the issuer.","time":"2020-04-11T13:25:44.283Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":16052,"level":30,"msg":"In BearerStrategy constructor: created strategy with options {\"identityMetadata\":\"https://login.microsoftonline.com/tenant1/v2.0/.well-known/openid-configuration\",\"clientID\":\"clientId2\",\"validateIssuer\":false,\"loggingLevel\":\"info\",\"passReqToCallback\":false,\"loggingNoPII\":false,\"clockSkew\":300,\"allowMultiAudiencesInToken\":false,\"audience\":[\"clientId2\",\"spn:clientId2\"]\"isB2C\":false,\"_isCommonEndpoint\":false}","time":"2020-04-11T13:25:44.285Z","v":0}
Listening on port 5000
UI (Angular SPA)
UI has permissions was granted automatically permission to access Microsoft Graph (profile, user.read, user.read.all -- last one I think I granted). The permissions are in "API permissions"
I went ahead and also granted access to the Backend access_as_user
For the UI code I'm using the MSAL library and again I pretty much copied the repo (https://github.com/Azure-Samples/active-directory-javascript-singlepageapp-angular)
In the protectedResourceMap field I added the following
['https://graph.microsoft.com/v1.0/me', ['user.read']],
['http://localhost:5000', ['api://clientId2/access_as_user']],
I am able to log in and read my user profile, but when trying to access http://localhost:5000/hello (protected), I'm getting the error the title of this question
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":20720,"level":30,"msg":"authentication failed due to: jwt audience is invalid","time":"2020-04-11T13:38:08.700Z","v":0}
--
I can see the Bearer Token coming (in the UI and backend), the server decodes the token (I can see all my profile info in the server logs), but it's saying the JWT is invalid?!
I'm not defining an audience, yet I can see in the token when it gets decoded the audience with aud: 'api://clientId2'.
I can also see when the backend starts it shows the audience as [clientId2, sps:clientId2] by default (step4 on the backend). When I define in the config audience: 'api://clientId2', I get a 403 with the message:
{"name":"AzureAD: Bearer Strategy","hostname":"DESKTOP-NCVLN56","pid":12644,"level":30,"msg":"In Strategy.prototype.jwtVerify: We did not pass Req back to Callback","time":"2020-04-11T16:19:30.398Z","v":0}
Any help would be appreciated. Thank you.
Turns out their code in the repository is not using proper configuration to verify the scope access...
https://github.com/Azure-Samples/active-directory-javascript-nodejs-webapi-v2/blob/master/index.js#L41
if (req.authInfo['scp'].split(" ").indexOf("demo.read") >= 0) {
I needed to change the scope from "demo.read" to "access_as_user".
In my case it was just that the clock of my VM where the application was running on was 15mins behind. So the time of the token was created was in the future...

Nodejs Loopback 4 add bearer token config into swagger explorer

I'm using Nodejs loopback 4 to build API project and using JWT token for authentication component. But when I explore built-in swagger of loopback (localhost:3000/explorer as default) then navigate to one of my API url, there is no input place for JWT Bearer Token. How can I config that let swagger display a JWT token input (that's just like it's display param query, request body input...)
Thanks in advance
Hello from the LoopBack team 👋
Authentication in general, and token-based authentication in particular, is something we are currently working on.
The problem of enabling token input from API Explorer has been discussed in loopback-next#2210. swagger-ui, the module powering our REST API explorer, does support token-based authentication. It requires the OpenAPI spec document describing application's API to also describe the authentication (security) schema used by the app.
So essentially, either the framework or the application needs to modify the OpenAPI spec to include OpenAPI's SecuritySchemeObject.
First, the security strategy must be defined in securityDefinitions section that's shared by all endpoints:
securityDefinitions:
petstore_auth:
type: "oauth2"
authorizationUrl: "http://petstore.swagger.io/oauth/dialog"
flow: "implicit"
scopes:
write:pets: "modify pets in your account"
read:pets: "read your pets"
api_key:
type: "apiKey"
name: "api_key"
in: "header"
The example above uses "oauth2" type. For JWT, you need to use "apiKey" type.
With the security type defined, you can reference it from endpoint definitions:
security:
- petstore_auth:
- "write:pets"
- "read:pets"
The following GitHub issue is keeping track of the work needed to enable
token based authentication in our REST API Explorer: loopback-next#2027. Feel free to subscribe to notifications or join the discussion there.
You may be interested in the following pull request too, it is adding support for JWT authentication to our Shopping example app: loopback4-example-shopping#26

How to have multiple authenticator in Symfony2 Security simple_preauth firewall

We are building a Symfony website where it will expose REST API for mobile app but with authentication token, which will autorize the Developer to consume the API, For this feature, I am using simple_preauth firewall authenticator, which works just fine. Firewall configuration :
firewalls:
app_authenticated:
pattern: ^/api
context: app
stateless: true
simple_preauth:
authenticator: api_key_authenticator
But our mobile app will offer end-user to login and we need the user's session to be stateless, which means, logged-in user's token has to be supplied along with developer's token, How do I implement multimple authenticator under simple_preauth firewall index?
I tried supplying [api_key_authenticator, api_key_authenticator2] as 2 authenticators but simple_preauth.authenticator requires value to be scalar, not array. Any help around ?
It is only possible since Symfony 2.8 using Guard authentication:
https://symfony.com/doc/2.8/security/guard_authentication.html
firewalls:
other_user_access:
pattern: ^/api/user/email$
guard:
authenticators:
- dev_authenticator
- api_prod_user_authenticator
- api_token_authenticator
entry_point: dev_authenticator

Resources