Cannot get flask-talisman to allow Firebase to run - javascript

I cannot get the Firebase "Sign in with Google" button to work using flask-talisman. Here is my CSP:
I am not sure if I a missing the right items for 'script-src'. One thing I do know is that if I remove this CSP from my application completely, everything works as normal. So I believe the Firebase issue has to do with what I am putting into the csp.
app = Flask(__name__)
csp = {
'default-src': [
'\'self\'',
'\'unsafe-inline\'',
'cdnjs.cloudflare.com',
'maxcdn.bootstrapcdn.com',
'ajax.googleapis.com',
'cdn.datatables.net',
'fonts.googleapis.com',
'gstatic.com',
'https://js.stripe.com',
],
'script-src': [
'\'self\'',
'unsafe-eval',
'\'unsafe-inline\'',
'cdn.firebase.com',
'*.firebaseio.com'
],
'object-src': [
'\'self\''
]
}
talisman = Talisman(app, content_security_policy=csp)
Here is my relevant code in HTML where I use Firebase. It was taken from Google's example.
<div id="firebaseui-auth-container"></div>
<button id="sign-out" hidden=true>Sign Out</button>
<div id="login-info" hidden=true>
<script src="{{ url_for('static', filename='script.js') }}"></script>
Then file "script.js" is below:
window.addEventListener('load', function () {
document.getElementById('sign-out').onclick = function () {
firebase.auth().signOut();
};
// FirebaseUI config.
var uiConfig = {
signInSuccessUrl: '/',
signInOptions: [
// Comment out any lines corresponding to providers you did not check in
// the Firebase console.
firebase.auth.GoogleAuthProvider.PROVIDER_ID
//firebase.auth.EmailAuthProvider.PROVIDER_ID,
//firebase.auth.FacebookAuthProvider.PROVIDER_ID,
//firebase.auth.TwitterAuthProvider.PROVIDER_ID,
//firebase.auth.GithubAuthProvider.PROVIDER_ID,
//firebase.auth.PhoneAuthProvider.PROVIDER_ID
],
// Terms of service url.
// tosUrl: '/terms-of-service',
privacyPolicyUrl: '/privacy-policy'
};
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in, so display the "sign out" button and login info.
document.getElementById('sign-out').hidden = false;
document.getElementById('login-info').hidden = false;
console.log(`Signed in as ${user.displayName} (${user.email})`);
user.getIdToken().then(function (token) {
// Add the token to the browser's cookies. The server will then be
// able to verify the token against the API.
// SECURITY NOTE: As cookies can easily be modified, only put the
// token (which is verified server-side) in a cookie; do not add other
// user information.
document.cookie = "token=" + token;
});
} else {
// User is signed out.
// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());
// Show the Firebase login button.
ui.start('#firebaseui-auth-container', uiConfig);
// Update the login state indicators.
document.getElementById('sign-out').hidden = true;
document.getElementById('login-info').hidden = true;
// Clear the token cookie.
document.cookie = "token=";
}
}, function (error) {
console.log(error);
alert('Unable to log in: ' + error)
});
});
Does anyone know what I am missing?

Related

Keycloak Javascript failed to inicialize

I'm trying to use Keycloak with JavaScript and these are the steps that I followed.
I create a client inside KeyCloak admin panel.
Link to image
I copy the .json file to my apache folder.
{
"realm": "master",
"auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external",
"resource": "test",
"public-client": true,
"confidential-port": 0
}
I go to my index.html and I add these two lines for calling the script.
<script src="keycloak.js"></script>
<script>
function initKeycloak() {
const keycloak = new Keycloak();
keycloak.init().then(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).catch(function() {
alert('failed to initialize');
});
}
</script>
this is what i have in myLogical.js
var keycloak = new Keycloak();
function initKeycloak() {
keycloak.init({onLoad: 'login-required'}).then(function() {
constructTableRows(keycloak.idTokenParsed);
pasteToken(keycloak.token);
}).catch(function() {
alert('failed to initialize');
});
}
function constructTableRows(keycloakToken) {
document.getElementById('row-username').innerHTML = keycloakToken.preferred_username;
document.getElementById('row-firstName').innerHTML = keycloakToken.given_name;
document.getElementById('row-lastName').innerHTML = keycloakToken.family_name;
document.getElementById('row-name').innerHTML = keycloakToken.name;
document.getElementById('row-email').innerHTML = keycloakToken.email;
}
function pasteToken(token){
document.getElementById('ta-token').value = token;
document.getElementById('ta-refreshToken').value = keycloak.refreshToken;
}
var refreshToken = function() {
keycloak.updateToken(-1)
I tried to download the file keycloak.js and put it directly on my root folder but it happen the same problem.
These is the message I got when I try to open the page
I'm confused about point 1, does keycloak automatically load configuration from json file in Apache folder? Let's assume that no, and I think that where your problem lies, you're not passing config param to keycloak constructor.
How to initialize keycloak:
const initKeycloak = async () => {
//you can hardcode these values for now just to see if everything works
const config = { url: 'http://localhost:8080/auth', realm: 'master', clientId: 'test'};
const keycloak = new Keycloak(config);
await keycloak
.init({ onLoad: 'login-required' })
.then(isAuthenticated => {
//user is authenticated
})
.catch(error => { console.log('keycloak error', error); });
}
Another important thing is that keycloak-js library version (in package.json) must match keycloak server version. Sometimes different versions work with each other but it's always best practice that keycloak-js version matches keycloak server version.
You can also look here: https://github.com/m-s7/react-core/blob/devel/src/services/keycloak-service.ts this is my repo with working keycloak-js implementation.

Blazor Client Side and Okta Widget

Trying to use JavaScript Interop using Blazor client side.
The widget doesn't render.
I was hoping to setup an employee portal in Blazor, but wanted to use the Okta widget of course. Initially I just couldn't get the widget to render and now more problems. Will have to back track a little, but has anyone a clue how to render a javascript UI component within Blazor?
Also, I replaced the Okta config info with my own Okta developer instance info - not shown below...
#inject IJSRuntime JSRuntime
<h3>Employee Login</h3>
<div id="okta-signin-widget"></div>
#code {
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
JSRuntime.InvokeAsync<object>("RenderLogin", "OnAfterRender was Triggered");
}
}
}
RenderLogin is a tag in a JavaScript file OktaLogin.js
Inside an OktaLogin.js file (everything in the file is client side):
signInWidgetConfig = {
// Enable or disable widget functionality with the following options. Some of these features require additional configuration in your Okta admin settings. Detailed information can be found here: https://github.com/okta/okta-signin-widget#okta-sign-in-widget
// Look and feel changes:
logo: '//logo.clearbit.com/okta.com', // Try changing "okta.com" to other domains, like: "workday.com", "splunk.com", or "delmonte.com"
language: 'en', // Try: [fr, de, es, ja, zh-CN] Full list: https://github.com/okta/okta-signin-widget#language-and-text
i18n: {
//Overrides default text when using English. Override other languages by adding additional sections.
'en': {
'primaryauth.title': 'Sign In', // Changes the sign in text
'primaryauth.submit': 'Sign In' // Changes the sign in button
// More e.g. [primaryauth.username.placeholder, primaryauth.password.placeholder, needhelp, etc.].
// Full list here: https://github.com/okta/okta-signin-widget/blob/master/packages/#okta/i18n/dist/properties/login.properties
}
},
// Changes to widget functionality
features: {
registration: true, // Enable self-service registration flow
rememberMe: true, // Setting to false will remove the checkbox to save username
//multiOptionalFactorEnroll: true, // Allow users to enroll in multiple optional factors before finishing the authentication flow.
//selfServiceUnlock: true, // Will enable unlock in addition to forgotten password
//smsRecovery: true, // Enable SMS-based account recovery
//callRecovery: true, // Enable voice call-based account recovery
router: true // Leave this set to true for the API demo
},
baseUrl: 'https://live-widget.oktapreview.com',
clientId: '0oaexo9c530ZUVuOj0h7',
redirectUri: 'https://developer.okta.com/live-widget',
authParams: {
issuer: 'https://live-widget.oktapreview.com/oauth2/ausexqn31sz3HMxdf0h7',
responseType: ['id_token', 'token'],
scopes: ['openid', 'email', 'profile']
}
};
signInWidget = new OktaSignIn(signInWidgetConfig);
function widgetSuccessCallback(res) {
var key = '';
if (res[0]) {
key = Object.keys(res[0])[0];
signInWidget.tokenManager.add(key, res[0]);
}
if (res[1]) {
key = Object.keys(res[1])[0];
signInWidget.tokenManager.add(key, res[1]);
}
if (res.status === 'SUCCESS') {
var token = signInWidget.tokenManager.get(key);
console.log("Logged in to Okta and issued token:");
console.log(token);
console.log("Reload this page to start over.");
alert("Logged in! Check your developer console for details");
}
}
function widgetErrorCallback(err) {
}
RenderLogin:** signInWidget.renderEl({ el: '#widget-container' }, widgetSuccessCallback, widgetErrorCallback);

ADAL.js Token renewal operation failed due to timeout Azure AD Auth Web API

We have experienced the following problem:
We had implemented Azure AD authenticated web API and we have been successfully had been making calls to it with ADAL 1.0.12 (we had our own wrapper around it for processing iframe for silent login).
Now we got rid all together of our wrapper and upgraded to the latest version and started to get the problem described here: token renewal operation timed out.
The Azure AD architecture behind the API is very simple: a registered web app with Read directory data and Sign in and read user profile permissions. We have another native app registered with permissions to the web app (Access to the Web APP NAME HERE). Both applications have oauth2AllowImplicitFlow set to true in their manifests.
Both apps have registered a wildcard redirect URIs for https://ourtenant.sharepoint.com/* (we are making the calls from SharePoint to the web API). In addition, the native client app has registered redirect URI to the unique URI (App ID URI) of the web API app.
Both apps have been granted admin consent before - they were working fine with the old version of ADAL.js
So, here is some code. Please, note that we had tried both without displayCall callback (with page refresh directly on the current page) and with page refresh (check the commented code in initLogin method). Also we had a switch for generating a popup or an iframe with a callback on successful login.
The problem is with authContext.acquireToken. Note that if we call OurNamespace.authContext.getCachedToken(OurNamespace.clientId) we get a stored token for the resource.
Note that also, we are calling properly handleWindowCallback after each page refresh / iframe / popup.
Happens regardless of the browser.
OurNamespace = {
serviceMainUrl: "https://localhost:44339",
webApiAppIdUri: "WEB-API-URI",
tenant: "TENANT",
clientId: "NATIVE-APP-GUID", // Native APP
adalEndPoints: {
get: null
},
adal: null,
authContext: null,
dummyAuthPage: null,
usePopup: true,
init: function () {
this.dummyAuthPage = "DummmyLogin.aspx";
OurNamespace.adalEndPoints.get = OurNamespace.serviceMainUrl + "/api/values";
OurNamespace.authContext = new AuthenticationContext({
tenant: OurNamespace.tenant + '.onmicrosoft.com',
clientId: OurNamespace.clientId,
webApiAppIdUri: OurNamespace.webApiAppIdUri,
endpoints: OurNamespace.adalEndPoints,
popUp: false,
postLogoutRedirectUri: window.location.origin,
cacheLocation: "localStorage",
displayCall: OurNamespace.displayCall,
redirectUri: _spPageContextInfo.siteAbsoluteUrl + "/Pages/" + OurNamespace.dummyAuthPage
});
var user = OurNamespace.authContext.getCachedUser(); // OurNamespace.authContext.getCachedToken(OurNamespace.clientId)
if (user) {
OurNamespace.azureAdAcquireToken();
} else {
OurNamespace.initLogin();
}
},
initLogin: function () {
//OurNamespace.authContext.config.displayCall = null;
//var isCallback = OurNamespace.authContext.isCallback(window.location.hash);
//OurNamespace.authContext.handleWindowCallback();
//if (isCallback && !OurNamespace.authContext.getLoginError()) {
// window.location.href = OurNamespace.authContext._getItem(OurNamespace.authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
//}
OurNamespace.authContext.login();
},
displayCall: function (url) {
var iframeId = "azure-ad-tenant-login",
popup = null;
if (OurNamespace.usePopup) {
popup = window.open(url, 'auth-popup', 'width=800,height=500');
} else {
var iframe = document.getElementById(iframeId);
if (!iframe) {
iframe = document.createElement("iframe");
iframe.setAttribute('id', iframeId);
document.body.appendChild(iframe);
}
iframe.style.visibility = 'hidden';
iframe.style.position = 'absolute';
iframe.src = url;
popup = iframe.contentDocument;
}
var intervalId = window.setInterval(function () {
try {
// refresh the contnetDocument for iframe
if (!OurNamespace.usePopup)
popup = iframe.contentDocument;
var isCallback = OurNamespace.authContext.isCallback(popup.location.hash);
OurNamespace.authContext.handleWindowCallback(popup.location.hash);
if (isCallback && !OurNamespace.authContext.getLoginError()) {
popup.location.href = OurNamespace.authContext._getItem(OurNamespace.authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
window.clearInterval(intervalId);
if (OurNamespace.usePopup) {
popup.close();
}
var user = OurNamespace.authContext.getCachedUser();
if (user) {
console.log(user);
} else {
console.error(OurNamespace.authContext.getLoginError());
}
}
} catch (e) { }
}, 400);
},
azureAdAcquireToken: function () {
OurNamespace.authContext.acquireToken(OurNamespace.adalEndPoints.get, function (error, token) {
if (error || !token) {
SP.UI.Status.addStatus("ERROR", ('acquireToken error occured: ' + error), true);
return;
} else {
OurNamespace.processWebApiRequest(token);
}
});
},
processWebApiRequest: function (token) {
// Alternatively, in MVC you can retrieve the logged in user in the web api with HttpContext.Current.User.Identity.Name
$.ajax({
type: "GET",
url: OurNamespace.adalEndPoints.get,
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {},
headers: {
'Authorization': 'Bearer ' + token
},
success: function (results) {
var dataObject = JSON.parse(results);
SP.UI.Status.addStatus("Info", "ADAL GET data success: " + dataObject.webApiUser);
$(".grid-info").html("<h1 class=\"h1\">Current Web API authenticated user: " + dataObject.webApiUser + "</h1>");
},
error: function (xhr, errorThrown, textStatus) {
console.error(xhr);
SP.UI.Status.addStatus("ERROR", ('Service error occured: ' + textStatus), true);
}
});
}
}
I am testing using the 1.0.15 adal.js and get the access token successfully by using the authContext.acquireToken which actually call the this._renewToken(resource, callback) to acquire the access token by the hide iframe. Here is the full test sample code for your reference:
<html>
<head>
<script src="/js/1.0.15/adal.js"></script>
<script>
var config = {
tenant: 'adfei.onmicrosoft.com',
clientId: '7e3b0f81-cf5c-4346-b3df-82638848104f',
redirectUri: 'http://localhost/spa.html',
navigateToLoginRequestUrl:false,
};
var authContext=new AuthenticationContext(config);
var hash = window.location.hash;
if(hash.length!=0){
authContext.handleWindowCallback(hash);
var user=authContext.getCachedUser();
}
function login(){
authContext.login();
}
function acqureToken(){
authContext.acquireToken("https://graph.microsoft.com", function(error, token, message){
console.log(token)
})
}
</script>
</head>
<body>
<button onclick="login()">Login</button>
<button onclick="acqureToken()">AcquireToken</button>
</body>
</html>
Is it helpful? Or would you mind sharing a run-able code sample for this issue?
I'm not sure if what version of adal.js you are using. But there is a loadFrameTimeout setting for the config object that you can set in milliseconds. Check the top of your adal.js file and it should be there.

how to link a new user to google analytics property/view using javascript

My requirement is, I need to add a new email id to an existing google analytics account's property.
function insertPropertyUserLink() {
var request = gapi.client.analytics.management.webpropertyUserLinks.insert(
{
'accountId': '123456',
'webPropertyId': 'UA-123456-1',
'resource': {
'permissions': {
'local': [
'EDIT',
'MANAGE_USERS'
]
},
'userRef': {
'email': 'liz#gmail.com'
}
}
});
request.execute(function (response) { // Handle the response. });
}
Above code i got from google documentation and i am using the following code for authorization:
<script>
var GoogleAuth;
var SCOPE = 'https://www.googleapis.com/auth/analytics.manage.users';
function handleClientLoad() {
// Load the API's client and auth2 modules.
// Call the initClient function after the modules load.
gapi.load('client:auth2', initClient);
}
function initClient() {
// Retrieve the discovery document for version 3 of Google Drive API.
// In practice, your app can retrieve one or more discovery documents.
var discoveryUrl = 'https://www.googleapis.com/analytics/v3/management/accounts/';
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes.
gapi.client.init({
'apiKey': 'mykey',
'discoveryDocs': [discoveryUrl],
'clientId': 'myclientId',
'scope': SCOPE
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
var user = GoogleAuth.currentUser.get();
setSigninStatus();
// Call handleAuthClick function when user clicks on
// "Sign In/Authorize" button.
$('#sign-in-or-out-button').click(function () {
handleAuthClick();
});
$('#revoke-access-button').click(function () {
revokeAccess();
});
});
}
function handleAuthClick() {
if (GoogleAuth.isSignedIn.get()) {
// User is authorized and has clicked 'Sign out' button.
GoogleAuth.signOut();
} else {
// User is not signed in. Start Google auth flow.
GoogleAuth.signIn();
}
}
function revokeAccess() {
GoogleAuth.disconnect();
}
function setSigninStatus(isSignedIn) {
var user = GoogleAuth.currentUser.get();
var isAuthorized = user.hasGrantedScopes(SCOPE);
if (isAuthorized) {
$('#sign-in-or-out-button').html('Sign out');
$('#revoke-access-button').css('display', 'inline-block');
$('#auth-status').html('You are currently signed in and have granted ' +
'access to this app.');
} else {
$('#sign-in-or-out-button').html('Sign In/Authorize');
$('#revoke-access-button').css('display', 'none');
$('#auth-status').html('You have not authorized this app or you are ' +
'signed out.');
}
}
function updateSigninStatus(isSignedIn) {
setSigninStatus();
}
</script>
<button id="sign-in-or-out-button"
style="margin-left: 25px">
Sign In/Authorize
</button>
<button id="revoke-access-button"
style="display: none; margin-left: 25px">
Revoke access
</button>
<div id="auth-status" style="display: inline; padding-left: 25px"></div><hr>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
I have changed the API key and client id with mine and enabled the Analytics API for the app in console. Can anyone help me to integrate the above two code snippets to a single one and can be able to add a new user to analytics property.
To solve this: **gapi.client.analytics is undefined**
Change:
`gapi.load(**'client:auth2'**, initClient);`
to
gapi.load(**'client:analytics'**, initClient);
It worked for me.

ReactJS app authentication: Firebase+FirebaseUI Uncaught Error: Firebase App named '[DEFAULT]-firebaseui-temp' already exists

I'm having trouble with my code. I'm building a one page web app in ReactJS with 3 tabs.
When the user goes to one tab, the authentication form from FirebaseUI should show up. The thing is that it's working only the first time and the second time, if I change to another tab and come back, it crashes, React re-renders the component that renders the div with the authentication form and throws the error:
"firebase.js:26 Uncaught Error: Firebase App named '[DEFAULT]-firebaseui-temp' already exists."
My index.html is :
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="./src/favicon.ico">
<script src="https://www.gstatic.com/firebasejs/3.4.1/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyAdyeoTYNF0xLK37Zv3nEGHWCKNPQjSPsI",
authDomain: "xxxx.com",
databaseURL: "xxxxx.com",
storageBucket: "xxxxxx.appspot.com",
messagingSenderId: "xxxxxx"
};
firebase.initializeApp(config);
</script>
<script src="https://www.gstatic.com/firebasejs/ui/live/0.5/firebase-ui-auth.js"></script>
<link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/live/0.5/firebase-ui-auth.css" />
<title>Flockin</title>
</head>
<body>
<div id="root" class="container"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` in this folder.
To create a production bundle, use `npm run build`.
-->
</body>
</html>
The module that has the Firebase code and fills the div is on another file on a modules directory:
var firebase=global.firebase;
var firebaseui=global.firebaseui;
var FirebaseUIManager=function(){
// FirebaseUI config.
var uiConfig = {
'signInSuccessUrl': '/archive',
'signInOptions': [
// Leave the lines as is for the providers you want to offer your users.
firebase.auth.FacebookAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID
],
// Terms of service url.
'tosUrl': '<your-tos-url>',
};
// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());
// The start method will wait until the DOM is loaded.
ui.start('#firebaseui-auth-container', uiConfig);
var initApp = function() {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
var displayName = user.displayName;
var email = user.email;
var emailVerified = user.emailVerified;
var photoURL = user.photoURL;
var uid = user.uid;
var providerData = user.providerData;
user.getToken().then(function(accessToken) {
document.getElementById('sign-in-status').textContent = 'Signed in';
document.getElementById('sign-in').textContent = 'Sign out';
document.getElementById('account-details').textContent = JSON.stringify({
displayName: displayName,
email: email,
emailVerified: emailVerified,
photoURL: photoURL,
uid: uid,
accessToken: accessToken,
providerData: providerData
}, null, ' ');
});
} else {
// User is signed out.
document.getElementById('sign-in-status').textContent = 'Signed out';
document.getElementById('sign-in').textContent = 'Sign in';
document.getElementById('account-details').textContent = 'null';
}
}, function(error) {
console.log(error);
});
};
initApp();
};
export default FirebaseUIManager;
And finally, the component that re-renders the form every time I go back to the tab on the componentDidMount method is:
import React, { Component } from 'react';
import FirebaseUIManager from './../modules/firebase-auth-login-manager.js';
class FlockinList extends Component {
componentDidMount(){
FirebaseUIManager();
}
render() {
return (
<div>
<div id="firebaseui-auth-container"></div>
<div id="sign-in-status"></div>
<div id="sign-in"></div>
<div id="account-details"></div>
</div>
);
}
}
export default FlockinList;
Any idea on how to solve this? Thanks!
You should not initialize a new FirebaseUI instance each time you show it:
var ui = new firebaseui.auth.AuthUI(firebase.auth());
This should be initialized externally.
If you wish to render, call
ui.start('#firebaseui-auth-container', uiConfig);
When you want to remove, call:
ui.reset();
But do not initialize a new instance each time.
you could use the useEffect hook to make sure the auth container is loaded when the start method is called :
useEffect(() => {
let ui = new firebaseui.auth.AuthUI(firebase.auth());
ui.start("#firebaseui-auth-container", uiConfig);
return () => {
ui.delete();
};
}, []);
remove the instance on unmounting.

Categories