I want to be able to request a distant API with my Angular 5 app.
The API server is asking a SSL-certificate in order to authenticate the client.
This certificate is distributed - by the company that provide the API - to a limited number of users, by handing them directly an USB key that contains the certificate (which is delivered by a trusted CA).
That means that I don't have access to a .pem or .crt file. Instead, the certificate is locally stored in the Windows AD (or local store) and I can't export the private key (for obvious security reason).
But the CA is also providing drivers that allow to register a certificate (if your USB key is plugged) to your favorite browser.
So I tried with the following test project:
app.component.ts
import {Component, OnInit} from '#angular/core';
import {Http, Response, RequestOptions, Headers} from '#angular/http';
import {HttpClient} from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'TEST API';
url = 'https://myAPIurl/lgcMessagerieInbox';
result = '';
constructor(private http: HttpClient) {
}
ngOnInit(): void {
}
testAPIcall(): void {
this.http.get(this.url)
.subscribe(
data => {
console.log(data);
},
error => {
console.log(error);
}
);
}
}
app.component.html
<div class="container">
<h1>
{{ title }}
</h1>
<button class="btnStandard" type="button" (click)="testAPIcall()" >TEST</button>
<div *ngIf="result">
{{ result }}
</div>
</div>
The first good news was that the browser automatically detected the fact that the API was asking a certificate and opened a windows so I could select a certificate, enter my pin code and send the http request.
But it failed because the server doesn't accept CORS requests ( https://www.html5rocks.com/en/tutorials/cors/ ).
So I though of using the local proxy provided by Angular in order to redirect the request so the browser doesn't block it because of different domain name. But then I wasn't able to pass the certificate through the proxy.
My questions are :
Is there a way to read the certificate directly on the windows local
store from my Angular app? I'm almost certain there is not if I
trust what I've been searching on internet over the past 3 days.
How can I pass a certificate through a proxy (Angular or anything else)?
If the two options above are not available, do you know a way to access the API knowing the constraints (using an other techno but it must be
cross-platform)?
Related
I am trying to connect to my Nestjs websocket with postman for rapid testing during development but I am having a lot of trouble getting postman to actually connect.
This is my errror
Error: connect ECONNREFUSED 127.0.0.1:8000
Here is my server code:
import { Logger } from '#nestjs/common';
import { OnGatewayConnection, OnGatewayInit, SubscribeMessage, WebSocketGateway } from '#nestjs/websockets';
#WebSocketGateway({ namespace: "test", cors: true })
export class AppGateway implements OnGatewayInit, OnGatewayConnection {
private logger: Logger
constructor() {
this.logger = new Logger('AppGateway')
}
afterInit(server: any) {
this.logger.log("Gateway is running")
}
handleConnection(client: any, ...args: any[]) {
this.logger.log("Client Connected")
}
#SubscribeMessage('message')
handleMessage(client: any, payload: any): string {
return 'Hello world!';
}
}
This is extremely simplistic because I actually spun up a new nestjs server incase it was something to do with authentication
Here is a picture of my postman UI to show what I am doing there (I am using socket-io v4 on my server which is why I have that option selected on postman)
I have also tried the url ws://127.0.0.1:8000/test but this produces the same error (also have tried using localhost instead of 127.0.0.1)
I know my websocket server is functioning correctly (and on the correct port) because I spun up a quick react app using the socketio client library and it connected to the websocket fine.
Here is the code to my quick react app:
import logo from './logo.svg';
import './App.css';
import { io } from 'socket.io-client'
function App() {
const socket = io("http://localhost:8000/test")
console.log(socket)
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
I have been stuck on this for a day or two now so any help would be great!
Thanks in advance.
I had a similar problem when running NestJS in WSL and trying to connect via Postman.
As far as I understood, there is a bug report that, when using socket.io feature, Postman attempts to convert localhost to 127.0.0.1 which does not work because it's not the WSL IP.
So, in order to make it work you need:
Open a PowerShell to get WSL IP (it changes every time WSL reboots):
wsl hostname -I
Use the output IP to connect via Postman:
http://<ip>:8000/test
About http or ws, using socket.io, it doesn't matter if you choose http or ws to connect, socket.io docs tells us the following
You can use either https or wss (respectively, http or ws).
I am having an issue with the Cloudinary Node SDK where the Admin Resources endpoint is occasionally throwing a 302 error. Their support suggested that I proxy the request so that I can log the response between my api and their SDK and ultimately get a better idea of what might be causing the problem (in the end they're hoping to see the Location headers that are in the Response).
One of their suggestions was to use Charles Proxy, however I'm very new to how this works and am unable to figure it out. I've read through the Charles docs and spent a full day googling, but I can't find any info related to proxying between the NextJS API and Cloudinary SDK specifically.
How do I go about setting up Charles Proxy so that I can see the request and response in this way? Is there another way that I don't know of that would work instead? Since I'm using the newest version of NextJS v12, could I use the new _middleware option instead? In a later suggestion, their Support made this comment too:
if you can't proxy requests to localhost, you may be able to use a local DNS server or a local override so you can access your local IP using a different hostname (e.g. using /etc/hosts on a unix environment, or C:\Windows\System32\Drivers\etc\hosts on a windows PC) and have that proxied - that said, there's probably an easier way using a Node project or adjusting the settings of the Node server.
but I have no idea where to begin with this either.
Here is the api code I have:
pages/api/auth/images.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import cloudinary from 'cloudinary';
require('dotenv').config();
export default async function handler(_: NextApiRequest, res: NextApiResponse) {
cloudinary.v2.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
secure: true,
});
try {
// fetch cloudinary auth images
const response = await cloudinary.v2.api.resources({
type: 'upload',
prefix: 'my_app/auth_pages',
max_results: 20,
});
// fetch random image
const randImage =
response.resources[~~(response?.resources?.length * Math.random())];
// return image
return res.status(200).json({ image: randImage });
} catch (error) {
console.dir({ error }, { colors: true, depth: null });
return res.status(500).json({ error });
}
}
Note: I'm on a Mac.
Try the following:
export default async function handler(_: NextApiRequest, res: NextApiResponse) {
cloudinary.v2.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
api_proxy: 'http://<local_ip>:<charles_port>', //change accordingly
secure: true,
});
To get the port number, In Charles Proxy go to Proxy > Proxy Settings.
I need to send a JWt (access token) to the chatbot via directline. I'm using react as the front end, and the chatbot is integrated into the front end via botframework-webchat.
So far, I was able to send the access token through an activity, which is not recommended as I think.
Right now, the chatbot is asking the user to log in, which is not good because the user is already logged in to the application.
My first question - Is it possible to authenticate the chatbot by an id token instead of connecting with Azure AD, B2C, or any auth service provider?
If it is possible, How can I send the id token to the bot, via botframework-webchat
Thanks in advance
Here is my code for the front end
const Chatbot = (props) => {
const language = localStorage.getItem('language');
const directLine = useMemo(
() => createDirectLine({ token: <my_token>, locale: 'sv-se' }),
[]
);
useEffect(() => {
var activity = {
from: {
id: '001',
name: 'noviral',
},
name: 'startConversation',
type: 'event',
value: 'Hi noviral!',
locale: language === 'en' ? 'en-US' : 'sv-se',
};
directLine.postActivity(activity).subscribe(function (id) {
if (console) {
console.log('welcome message sent to health bot');
}
});
}, []);
return (
<Layout className="login-layout">
<div className="login-div">
<div className="chatbot">
<div className="consent-wrapper">
<ReactWebChat
directLine={directLine}
userID={'001'}
username="Noviral"
locale={language === 'en' ? 'en-US' : 'sv-se'}
></ReactWebChat>
</div>
</div>
</div>
</Layout>
);
};
export default withTranslation()(Chatbot);
Sending the token via an activity is acceptable as activities sent via Direct Line are secure. If you look over the 24.bot-authentication-msgraph sample, you can see that the default action the bot takes is to send an activity displaying the user's token.
As for authentication, the question doesn't seem to be what token you will use but rather how you will authenticate. If you don't use a service provider + login, how is the bot going to verify who the user is? That being said, there are some SSO (single sign-on) options available via Web Chat (see here) that, if a user is already logged in, then SSO could pick it up. You will have to look them over to decide if these options meet your needs.
I'm creating an web application in Angular2 and i'd like to use Google for the user to login and use the application. Basically, once the user is logged in with Google, he will navigate in the site and make some AJAX call via Angular to the ASP.NET Core Web API.
In my mind, I though that all these calls should contain a JWT (issued by Google) and the ASP.NET Core Web Api would validate the token and then process the request if the token is valid. Simple enough... However, I'm struggling to find out how this could be achieved. All I found is posts talking about how to login via Google in order to use Google APIs. In my case, I don't need any Google API but the login one.
So far, I have this:
export class GoogleAuthenticationService {
private auth: any;
constructor(private userService: UserService) {
}
public bindLoginButton(element: any) {
this.ensureApiIsLoaded().then(() => {
this.auth.attachClickHandler(element, {}, (user) => {
let profile = user.getBasicProfile();
this.userService.set(profile.getName(), profile.getEmail(), user.getAuthResponse().id_token, AuthenticationType.Google);
});
});
}
// TODO: Better type definition of the promise
private ensureApiIsLoaded() {
return new Promise((resolve, reject) => {
gapi.load('auth2', () => {
this.auth = gapi.auth2.init({
client_id: "CLIENT_ID",
cookiepolicy: 'single_host_origin',
scope: 'profile email'
});
resolve();
});
});
}
}
Basically, I'm just initializing "gapi" with my client ID, then I define some properties in the "UserService". This works pretty well. Moreover, I'm using the value returned by user.getAuthResponse().id_token for every call to my web service (I did this following this article). The next step was to try and validate the token in C# (like the sample in Java) but I didn't find any valid resources talking about that and the class used in the Java code does not exists in C#...
Is it that complicated or am I just on the wrong path and this is totally not how the web application is supposed to work. Could someone give me some pointers about this scenario that is, according to me, quite common.
I am currently trying to use gapi.auth2 from Google Sign-In for Websites API and this is the code I have:
-- load the library with:
<script src="https://apis.google.com/js/platform.js?onload=onLoadGapiCallback" async defer></script>
-- initialize an auth2 variable:
var auth2;
window.onLoadGapiCallback = () => {
gapi.load('auth2', () => {
auth2 = gapi.auth2.init({
'client_id': 'CLIENT_ID',
'scope': 'profile email https://www.googleapis.com/auth/youtube.readonly'
});
});
};
-- and when a botton is clicked do:
auth2.signIn().then(() => {
console.log('auth is:', auth2.currentUser.get().getAuthResponse().access_token);
});
This works well, it initializes the auth2 variable, when I click the button, it shows the SingIn prompt and I choose one of my Google Accounts. The problem is from now on when I have to choose a YouTube account, if I choose other account than the main one, I'll get an Exception Object like this one:
{type: "tokenFailed", idpId: "google", error: "USER_LOGGED_OUT"}
also there's an XHR request being sent lastly that has this response:
{"error":"USER_LOGGED_OUT","detail":"No active session found."}
So it only works if I choose the main account, but I cannot choose other YouTube accounts.
What am I missing here?
I've looked into all these docs but none helped me:
Getting profile information
Google Sign-In JavaScript client reference
Monitoring the user's session state
Update:
Running the code from this example (but with this scope: 'profile email https://www.googleapis.com/auth/youtube.readonly') will only work if I choose the first Youtube account for each Google account. If I choose any other Youtube account, I'll get this alert error: