How to use google recaptcha in nuxt? - javascript

I am using nuxt and would like to use this library: https://github.com/nuxt-community/recaptcha-module. But I don't understand how to verify that the user has passed the check. The example doesn't tell me too much (https://github.com/nuxt-community/recaptcha-module/blob/master/example/v3/pages/index.vue). Could someone show me how to do it correctly?

This example is only half the story. It returns a Recaptcha V3 token on the client-side.
This must then be sent to the serverside and verified using your secret key.
This is done by sending a post to this URL:
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${token}`;
You do not want to allow this secret key on the client side.
To achieve this in Nuxt, assuming version 2.13+, you can utilise privateRuntimeConfig in your nuxt config.
This will allow you to link a .env file to be injected only on the server side.
For this use case, a privateRuntimeConfig like this should suffice:
privateRuntimeConfig: {
secretKey: process.env.GOOGLE_SECRET
}
Once you have done this, you will be able to access these variables as part of this.$config within your Nuxt application - in this case this.$config.secretKey when calling the Recaptcha verify endpoint.
For more information check out the Nuxt blog

Use https://github.com/nuxt-community/recaptcha-module, in your nuxt.config.js
modules: [
'#nuxtjs/recaptcha',
],
recaptcha: {
hideBadge: true,
siteKey: "ABC...", // Better would be from 'process.env.API_KEY' and with '.env' file
version: 2, // Or 3
},
Keep in mind that modules, that's not the same as buildModules (sometimes it might confuse due to similar naming).

Here is a working implementation for ReCaptcha V3:
package.json
"dependencies": {
"#nuxtjs/axios": "^5.13.6",
"#nuxtjs/recaptcha": "^1.0.4",
"h3": "^0.3.9",
},
Note the h3 version. I wasn't able to get it working with a newer version of that because the library is converted to EJS/mjs and TypeScript, which conflicts with Nuxt. Transpiling h3 didn't fix it. It may work with Nuxt V3+.
nuxt.config.js
modules: [
['#nuxtjs/recaptcha', {
siteKey: process.env.RECAPTCHA_SITE_KEY,
version: 3,
}],
],
serverMiddleware: [
{ path: '/api/check-token', handler: '~/middleware/recaptcha' },
],
middleware/recaptcha.js
import { useBody } from 'h3';
import axios from 'axios';
export default async (req, res) => {
res.setHeader('Content-Type', 'application/json');
try {
const { token } = await useBody(req);
if (!token) {
res.end(
JSON.stringify({
success: false,
message: 'Invalid token'
})
);
return;
}
axios.get(`https://www.google.com/recaptcha/api/siteverify?secret=${process.env.RECAPTCHA_SECRET_KEY}&response=${token}`).then((answer) => {
if (answer.status) {
res.end(
JSON.stringify({
success: true,
message: 'Token verified'
})
);
} else {
res.end(
JSON.stringify({
success: false,
message: 'Invalid token'
})
);
}
});
} catch (e) {
console.log('ReCaptcha error:', e);
res.end(
JSON.stringify({
success: false,
message: 'Internal error'
})
);
}
};
.env
RECAPTCHA_SITE_KEY=gosei478htosvei478tvoei478tvge
RECAPTCHA_SECRET_KEY=ios47eos487t6es4897gtv6es487
index.vue
async mounted() {
try {
await this.$recaptcha.init();
} catch (err) {
throw new Error(`index# Problem initializing ReCaptcha: ${err}.`);
}
},
beforeDestroy() {
this.$recaptcha.destroy();
},
methods: {
async submitContactForm() {
try {
const token = await this.$recaptcha.execute('contact')
const formData = {
email: this.contactForm.email,
firstname: name.firstName,
lastname: name.lastName,
phone: this.contactForm.phone,
band_name: this.contactForm.band_name,
initial_message: this.contactForm.message,
}
// note: use POST request
const recaptcha = await this.$axios.post('/api/check-token', { token });
console.log('recaptcha', recaptcha.data);
if (recaptcha.data.success) {
const result = await this.$axios.post(process.env.CONTACT_FORM_API, formData);
// cleanup logic
} else {
// handle error case
}
} catch (err) {
// handle errors
}
},
},
You can read more here: https://www.npmjs.com/package/#nuxtjs/recaptcha
Note the section where it says
Server Side
When you send data + token to the server, you should verify the token on the server side to make sure it does not requested from a bot. You can find out how to verify token on the server side by looking at the server middleware inside v2 example. (The server side is same for both versions)
The above server-side middleware comes from there. It is important to use the version of h3 that I suggest because you need it to access useBody(req). I tried for several hours to find another way to read the request body but it proved too difficult. Your results may vary in a newer version of Nuxt. I suggest trying the newest version of h3 and if that fails with errors when building the application, try the older version.
It is critically important to not expose the ReCaptcha secret key, and this solution keeps it secret in the server-side.
A more optimal solution might be to use your actual server and make an endpoint for validating ReCaptcha tokens. This above solution allows you to do it purely client-side, assuming you are using SSR.

Related

Is there a way to use firebase secret environment variables in slack/bolt

We operate bots by combining Firebase and Slack/bolt.
I currently use functions.config()to manage my Slack tokens and secrets, but would like to migrate to using Secret Manager.
https://firebase.google.com/docs/functions/config-env#secret-manager
boltapp.js
const functions = require("firebase-functions");
const { App, ExpressReceiver, subtype } = require("#slack/bolt");
const config = functions.config();
const expressReceiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SECRET,
endpoints: "/events",
processBeforeResponse: true,
});
const app = new App({
receiver: expressReceiver,
token: process.env.SLACK_TOKEN
});
app.error(error => { console.error(error) });
app.use(async ({ client, payload, context, next }) => {
console.info('It\'s payload', JSON.stringify(payload))
if (!context?.retryNum) {
await next();
} else {
console.debug('app.use.context', context);
}
});
//**bot processing**//
// https://{your domain}.cloudfunctions.net/slack/events
module.exports = functions
.runWith({ secrets: ["SLACK_TOKEN","SLACK_SECRET"] })
.https.onRequest(expressReceiver.app);
But when I rewrote it for migration, I got the following error.
Is there a way to rewrite the code while avoiding this error?
Failed to load function definition from source: FirebaseError: Failed to load function definition from source: Failed to generate manifest from function source: Error: Apps used in a single workspace can be initialized with a token. Apps used in many workspaces should be initialized with oauth installer options or authorize.
Since you have not provided a token or authorize, you might be missing one or more required oauth installer options. See https://slack.dev/bolt-js/concepts#authenticating-oauth for these required fields.

How could I obtain the user's gender and birthday after they sign in with their Google account?

I have followed the example in Display the Sign In With Google button to get a Google sign in button working in my Angular application:
<div id="g_id_onload"
class="mt-3"
data-client_id="XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com"
data-login_uri="http://localhost:1337/login/google"
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-width="250"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="continue_with"
data-shape="rectangular"
data-logo_alignment="center">
</div>
Once the user signs in, I verify and decode the JWT token provided by Google in my Express server using jsonwebtoken:
app.post('/login/google', express.urlencoded(), async(request, response, next) => {
try {
console.log(`${request.method} ${request.url} was called.`);
let token: string = request.body.credential;
let body: Response = await fetch('https://www.googleapis.com/oauth2/v1/certs', { method: 'GET', headers: { Accept: 'application/json' }});
let json: any = await body.json();
let certificates: string[] = Object.keys(json).map(key => json[key]);
let decoded: any;
let lastError: any;
certificates.every(certificate => {
try {
decoded = jwt.verify(token, certificate, { algorithms: ['RS256'], ignoreExpiration: false });
}
catch (error) {
lastError = error;
}
return !decoded;
});
if (!decoded)
throw lastError;
}
catch (error) {
next(error);
}
});
The problem is that the decoded token does not contain the user's gender or birthday information. How can I obtain this data?
I have just recently tried manually appending the https://www.googleapis.com/auth/user.birthday.read and https://www.googleapis.com/auth/user.gender.read scopes to my application's OAuth Consent Screen found at https://console.cloud.google.com/apis/credentials/consent/edit, but I don't see the user being prompted to provide this data to my application when it runs. I tried deleting permissions to my application from my account at accounts.google.com (under the Third-Party Access section) as well in hopes that it might prompt for these extra pieces of data. I am not sure at this point how to go about getting this extra data because I can't seem to find a good documentation piece on how to achieve this. Also, I wanted to add that my test account's Gender and Birthday information is set to be Private in https://myaccount.google.com/personal-info. I was wondering if its possible to fetch these private scopes somehow.
So, just to be clear, when I try to sign in I still only get the following prompt, which makes me believe that something is wrong and its not actually requesting the scope for birthday and gender from the user:
Confirm you want to sign in to [Application Name] with [User's Name].
To create your account, Google will share your name, email address,
and profile picture with [Application Name].
I also tried going on https://developers.google.com/oauthplayground/ and I pasted this in for Input your own scopes: https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/userinfo.profile,https://www.googleapis.com/auth/user.birthday.read,https://www.googleapis.com/auth/user.gender.read. I then hit the Authorize API button, logged in, granted access to these scopes (was prompted correctly on the playground), performed the token exchange, then I tried to List possible operations and under the People API, I called the get people endpoint, and modified the URI to https://people.googleapis.com/v1/people/me as per the documentation. This endpoint seems to work to fetch the data I need, but now I can't seem to wrap my head around what authorization parameters to use for this endpoint from the data I get back from the POST to my Express server. I have also tried enabling the People API from Enabled APIs & services.
You are using signin. Signin is open id connect and returns an id token. Id tokes contain very few claims. Gender is not one of them.
The only way to get access to the full user profile info is to go though the people api as you have mentioned.
You can use the try me to see it working and generate the sample for you.
<script src="https://apis.google.com/js/api.js"></script>
<script>
/**
* Sample JavaScript code for people.people.get
* See instructions for running APIs Explorer code samples locally:
* https://developers.google.com/explorer-help/code-samples#javascript
*/
function authenticate() {
return gapi.auth2.getAuthInstance()
.signIn({scope: "https://www.googleapis.com/auth/contacts https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/directory.readonly https://www.googleapis.com/auth/user.addresses.read https://www.googleapis.com/auth/user.birthday.read https://www.googleapis.com/auth/user.emails.read https://www.googleapis.com/auth/user.gender.read https://www.googleapis.com/auth/user.organization.read https://www.googleapis.com/auth/user.phonenumbers.read https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"})
.then(function() { console.log("Sign-in successful"); },
function(err) { console.error("Error signing in", err); });
}
function loadClient() {
gapi.client.setApiKey("YOUR_API_KEY");
return gapi.client.load("https://people.googleapis.com/$discovery/rest?version=v1")
.then(function() { console.log("GAPI client loaded for API"); },
function(err) { console.error("Error loading GAPI client for API", err); });
}
// Make sure the client is loaded and sign-in is complete before calling this method.
function execute() {
return gapi.client.people.people.get({
"resourceName": "people/me",
"personFields": "genders"
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error", err); });
}
gapi.load("client:auth2", function() {
gapi.auth2.init({client_id: "YOUR_CLIENT_ID"});
});
</script>
<button onclick="authenticate().then(loadClient)">authorize and load</button>
<button onclick="execute()">execute</button>
The issue that you are then going to have is the above sample uses Oauth2 and not open id connect (signin) It needs an access token to work. If you check your code I belive that the signin does return an access token. Your job then is to feed the access token to the code above so that you dont have to go though the authorization process again.
So far i have not found anyone able to link the new signin system with the old oauth2 system. If you get it to work i would love to see it.
Html
To call this api you need an access_token. a google access token is not a jwt. it is not the id_token
GET https://people.googleapis.com/v1/people/me?personFields=genders&key=[YOUR_API_KEY] HTTP/1.1
Authorization: Bearer [YOUR_ACCESS_TOKEN]
Accept: application/json
I finally managed to get it working with the help of this guide.
I had to scrap the idea of using the Google sign in button because it does not seem to allow extended scopes such as birthday and gender (well, not if they're private anyways - if anyone finds a way of doing it with the sign in button, please post an answer). Luckily, their OAuth API does support extended scopes. As such, I've implemented my own Google sign in button using the googleapis package.
There are a few steps to this:
Use the googleapis package to generate a URI to present to the user that will ask them to consent to gender and birthday access.
For example:
app.get('/login/google/uri', async(request, response, next) => {
try {
console.log(`${request.method} ${request.url} was called.`);
let client = new google.auth.OAuth2(
'ClientID',
'ClientSecret',
`http://localhost:4200/login/google/redirect`
);
const scopes = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/user.birthday.read',
'https://www.googleapis.com/auth/user.gender.read'
];
const authorizationUrl: string = client.generateAuthUrl({
access_type: 'offline',
scope: scopes,
include_granted_scopes: false
});
response.status(200).send({ uri: authorizationUrl });
}
catch (error) {
next(error);
}
});
Ensure that http://localhost:4200/login/google/redirect (or whatever redirect URI you use) is part of your OAuth 2.0 Client ID Credential's Authorized redirect URIs in the console.
Google will redirect to your redirect URI (http://localhost:4200/login/google/redirect) with a query parameter named code. For example: http://localhost:4200/login/google/redirect?code=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&scope=email%20profile%20https:%2F%2Fwww.googleapis.com%2Fauth%2Fuser.gender.read%20https:%2F%2Fwww.googleapis.com%2Fauth%2Fuser.birthday.read%20https:%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https:%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20openid&authuser=0&prompt=consent
Take the code (XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX) and exchange it for an access token.
For example:
let client = new google.auth.OAuth2(
'ClientID',
'ClientSecret',
`http://localhost:4200/login/google/redirect`
);
let code: string = request.params.code;
let { tokens } = await client.getToken(code);
console.log(tokens.access_token);
Use the access_token (it looks something like XXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX) when making requests to the People API and set it in the Authorization header as the bearer token.
For example:
curl "https://people.googleapis.com/v1/people/me?key=XXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXX&personFields=genders,birthdays" -H "Authorization: Bearer XXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
key is your API key from the console (you can create one and restrict it to the People API - if you don't see the People API as a restriction option you might need to enable it from the Enabled APIs and services tab). I'm sure there is a more API friendly way of making this request in the googleapis package that you can explore, but I just wanted to highlight how it works with curl.
The response you will see should be like this:
{
"resourceName": "people/XXXXXXXXXXXXXXXXXXXX",
"etag": "XXXXXXXXXXXXXXXXXXXXX",
"genders": [
{
"metadata": {
"primary": true,
"source": {
"type": "PROFILE",
"id": "XXXXXXXXXXXXXXXXXXXX"
}
},
"value": "male",
"formattedValue": "Male"
}
],
"birthdays": [
{
"metadata": {
"primary": true,
"source": {
"type": "ACCOUNT",
"id": "XXXXXXXXXXXXXXXXXXXX"
}
},
"date": {
"year": 1901,
"month": 1,
"day": 1
}
}
]
}
Edit: Just for completion, here is the API friendly way to do all of this.
First, generate this URI and redirect the user to it:
app.get('/login/google/uri', async(request, response, next) => {
try {
console.log(`${request.method} ${request.url} was called.`);
let client = new googleapis.Auth.OAuth2Client(
Globals.GoogleClientID,
Globals.GoogleClientSecret,
`${Globals.UIHost}/login`
);
const scopes = [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/user.birthday.read',
'https://www.googleapis.com/auth/user.gender.read'
];
const authorizationUrl: string = client.generateAuthUrl({
access_type: 'offline',
scope: scopes,
include_granted_scopes: false
});
response.status(200).send({ uri: authorizationUrl });
}
catch (error) {
next(error);
}
});
Second, after the user has signed in and you get a code posted back to your redirect URI, parse the query param for the code and use it like how I am doing so in the following POST method on my server to get these extra user details for birthdays, genders, and emails:
app.post('/login/google', express.json(), async(request, response, next) => {
try {
console.log(`${request.method} ${request.url} was called.`);
let client = new googleapis.Auth.OAuth2Client(
Globals.GoogleClientID,
Globals.GoogleClientSecret,
`${Globals.UIHost}/login`
);
let code: string = request.body.code;
let { tokens } = await client.getToken(code);
let accessToken: string = tokens.access_token;
client.setCredentials({ access_token: accessToken });
let people = new googleapis.people_v1.People({});
let result = await people.people.get({
resourceName: 'people/me',
personFields: 'emailAddresses,birthdays,genders',
auth: client
});
console.log(result.data);
}
catch (error) {
next(error);
}
});
result.data should contain the information.
If you are using NestJS with typescript, this worked for me
#Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor(configService: ConfigService) {
super({
clientID: configService.get('GOOGLE_CLIENT_ID'),
clientSecret: configService.get('GOOGLE_SECRET'),
callbackURL: configService.get('GOOGLE_REDIRECT_URL'),
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/plus.login',
'https://www.googleapis.com/auth/user.birthday.read',
'https://www.googleapis.com/auth/user.phonenumbers.read',
'https://www.googleapis.com/auth/user.gender.read',
],
});
}
async validate(
accessToken: string,
refreshToken: string,
profile: any,
done: VerifyCallback,
): Promise<any> {
const { name, emails, photos, sub, birthday, phoneNumber, gender } =
profile;
const user = {
sub,
email: emails[0].value,
firstName: name.givenName,
lastName: name.familyName,
picture: photos[0].value,
dob: birthday,
phoneNumber,
gender,
refreshToken,
accessToken,
};
done(null, user);
}
}
Then add GoogleStrategy to your provider. Of course, don't forget your keys in your .env file.

How to integrate Dialogflow v2 with javascript frontend (Vue.js)

I'm trying to integrate Dialogflow with Vue.js (and axios) according to the documentation's sample HTTP request: https://dialogflow.com/docs/reference/v2-auth-setup and detectIntent: https://dialogflow.com/docs/reference/api-v2/rest/v2beta1/projects.agent.sessions/detectIntent.
I have a service account set up with sufficient permissions, and given it the path parameters and request body as shown in the documentation, but I keep getting 'Error: Request failed with status code 400' when calling the detectIntent API.
There are a few things I'm not sure of, though:
How do I get a sessionId? Currently I just copy the sessionId from Firebase Function logs which shows up when entering a query through the Dialogflow console directly.
How do I actually implement $(gcloud auth print-access-token) in javascript code? Currently I'm running the command in the terminal and pasting the token in the code, just to test if the API works, but I have no clue how it should be implemented.
(Perhaps useful, I have fulfillment set up in a functions folder, and that is working nicely.)
Thanks in advance!
<script>
import axios from 'axios'
export default {
name: 'myChatBot',
mounted () {
// Authorization: Bearer $(gcloud auth print-access-token)
const session = 'projects/mychatbot/agent/sessions/some-session-id'
const token = 'xxxxxxxxxxxx'
axios.defaults.baseURL = 'https://dialogflow.googleapis.com'
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`
axios.defaults.headers.post['Content-Type'] = 'application/json'
axios
.post(`/v2beta1/${session}:detectIntent`, {
"queryInput": {
"text": "add buy milk to inbox",
"languageCode": "en-US"
}
})
.then(response => console.log(response))
.catch(error => console.log(error))
}
}
</script>
You can use JWT authorization to handle your #2 question. You just need to put your JSON file someplace safe. https://developers.google.com/identity/protocols/OAuth2ServiceAccount#jwt-auth
The reason you are getting the code 400 is because your params are a little off. Here is how your post should look (I've also added some extra code to handle token generation):
<script>
import axios from 'axios'
import { KJUR } from 'jsrsasign'
const creds = require('./YOUR_JSON_FILE')
export default {
name: 'myChatBot',
data() {
return {
token: undefined,
tokenInterval: undefined
}
},
created() {
// update the tokens every hour
this.tokenInterval = setInterval(this.generateToken, 3600000)
this.generateToken()
},
mounted () {
this.detectIntent('add buy milk to inbox')
},
beforeDestroy() {
clearInterval(this.tokenInterval)
},
methods: {
generateToken() {
// Header
const header = {
alg: 'RS256',
typ: 'JWT',
kid: creds.private_key_id
}
// Payload
const payload = {
iss: creds.client_email,
sub: creds.client_email,
iat: KJUR.jws.IntDate.get('now'),
exp: KJUR.jws.IntDate.get('now + 1hour'),
aud: 'https://dialogflow.googleapis.com/google.cloud.dialogflow.v2.Sessions'
}
const stringHeader = JSON.stringify(header)
const stringPayload = JSON.stringify(payload)
this.token = KJUR.jws.JWS.sign('RS256', stringHeader, stringPayload, creds.private_key)
},
detectIntent(text, languageCode = 'en-US') {
if (!this.token) {
// try again
setTimeout(this.detectIntent, 300, text, languageCode)
return
}
// error check for no text, etc.
const session = 'projects/mychatbot/agent/sessions/some-session-id'
axios.defaults.baseURL = 'https://dialogflow.googleapis.com'
axios.defaults.headers.common['Authorization'] = `Bearer ${this.token}`
axios.defaults.headers.post['Content-Type'] = 'application/json'
axios
.post(`/v2beta1/${session}:detectIntent`, {
queryInput: {
text: {
text,
languageCode
}
}
})
.then(response => console.log(response))
.catch(error => console.log(error))
}
}
}
</script>
You can see that in QueryInput it's taking 1 of 3 different types of objects ("text" being one of those).
In the link, it's stated under the HTTP request session path parameters that "It's up to the API caller to choose an appropriate session id.
It can be a random number or some type of user identifier (preferably hashed).
For integration with Dialogflow V2, here's an example for doing with third-party tools that are easy to integrate and start using.
The sessionId is an identifier you can provide that will indicate to Dialogflow whether subsequent requests belong to the same "session" of user interaction (see docs).
For a client's first request to the API, you could just generate a random number to use as a session ID. For subsequent requests from the same client (e.g. if a user is continuing to converse with your agent) you can reuse the same number.
Your implementation of the token management looks fine, as long as the service account you are using has appropriately limited access (since this token could potentially allow anyone to make requests to Google Cloud APIs). For additional security, you could consider proxying the request to Dialogflow through your own server rather than making the call from the client.

React JS - IBM Watson Personality InsightsV3 Token Auth - CORS error

I have been attempting to implement the Watson PersonalityInsightsV3 API into my React JS app. In order to circumvent the CORS error that arises when using credentials I have a flask/python server making a request for the token. I then use fetch inside my React JS files to receive the token and attempt to pass it to my Watson profile request. I am still getting the CORS error despite this and am not sure how to complete the request with the token.
try { axios.get('http://project1-m6.c9users.io:8080/token')
.then(result => { console.log('AAA ' + result.data)
var PersonalityInsightsV3 = require('watson-developer-cloud/personality-insights/v3');
var personality_insights = new PersonalityInsightsV3({
username: 'myname',
password: 'mypassword',
version_date: '2017-10-13',
headers: { 'X-Watson-Authorization-Token': result.data }
});
personality_insights.profile(
{
content: input,
content_type: 'text/plain',
consumption_preferences: true,
raw_scores: true
},
function(err, response) {
if (err) {
console.log('error:', err);
} else {
console.log(JSON.stringify(response, null, 2));
_this.props.history.push(
{pathname: '/personality', state: {result:JSON.stringify(response, null, 3)}})
}
}
);
}
);
} catch(e) { console.log(e) };
This may not be possible according to the docs:
https://www.npmjs.com/package/watson-developer-cloud#client-side-usage
Some endpoints/services don't support cors, so you may have to do some more proxying in your Python app.
I don't know what the popular packages are for that in Py, but checkout express-http-proxy.

OktaAuth Javascript + Flask not sending code & state value to Flask

I am new to okta, javascript. I am trying to add OktaAuth to existing Jquery/javascript + Flask app.
I have configured the following in my Javascript. The redirect calls to server-side callback works. But don't pass code, state values for it to proceed. Can you please let me know what is wrong here? Any help is appreciated.
var authClient = new OktaAuth({
url: 'https://{okta-url}.com',
clientId: 'xxxxxx',
clientSecret: 'yyyyyyyyyy',
issuer: 'https://{okta-url}.com',
redirectUri: 'http://{redirect-url}/login'
//scopes: ['openid', 'email', 'profile']
});
var idToken = authClient.tokenManager.get('idToken');
console.log(JSON.stringify(idToken))
if (idToken) {
console.log('hi ${idToken.claims.email}!');
}
else if (location.hash) {
authClient.token.parseFromUrl()
.then(function (idToken) {
console.log('hi ${idToken.claims.email}!');
authClient.tokenManager.add('idToken', idToken);
console.log(idToken);
});
console.log(JSON.stringify(authClient.token))
}
else {
authClient.token.getWithRedirect({
responseType: ['id_token', 'code', 'token']
});
console.log(authClient.token)
}
Warning: It's dangerous to have your client secret in JavaScript code! Exposing a client secret is like revealing your password. You should remove it and consider generating a new client ID/secret just to be safe.
There's two main ways you can use OpenID Connect, which is what OktaAuth uses: with a server-side callback (the code flow), or entirely on the client-side (the implicit flow). You're trying to do both here, which is probably why it's acting weird.
Instead, do this:
var authClient = new OktaAuth({
url: 'https://{okta-url}.com',
clientId: 'xxxxxx',
issuer: 'default', // Use the default Authorization Server
});
var idToken = authClient.tokenManager.get('idToken');
if (idToken) {
console.log('hi ${idToken.claims.email}!');
}
else if (location.hash) {
authClient.token.parseFromUrl()
.then(function (idToken) {
authClient.tokenManager.add('idToken', idToken);
console.log(idToken);
});
}
else {
authClient.token.getWithRedirect({
responseType: 'id_token'
// Use ['id_token', 'token'] if you also need an access token
});
}
This will get you an ID token that you can use on the client side. Watch the console and network panels for any errors that occur.

Categories