Application-Level Implementation
Application-Level Implementation
Node.js/Express Implementation
// Basic implementation
app.use((req, res, next) => {
res.setHeader('Permissions-Policy',
'camera=(), microphone=(), geolocation=(), payment=()'
);
next();
});
// Dynamic policy based on route
const permissionsPolicies = {
default: {
camera: [],
microphone: [],
geolocation: [],
payment: [],
usb: []
},
videoChat: {
camera: ['self'],
microphone: ['self'],
'display-capture': ['self'],
fullscreen: ['self']
},
maps: {
geolocation: ['self', 'https://maps.googleapis.com']
},
checkout: {
payment: ['self', 'https://stripe.com', 'https://paypal.com']
}
};
function buildPermissionsPolicy(config) {
return Object.entries(config)
.map(([feature, allowlist]) => {
const sources = allowlist.length > 0 ? allowlist.join(' ') : '';
return `${feature}=(${sources})`;
})
.join(', ');
}
app.use((req, res, next) => {
let policyConfig = permissionsPolicies.default;
if (req.path.startsWith('/video-chat')) {
policyConfig = { ...policyConfig, ...permissionsPolicies.videoChat };
} else if (req.path.startsWith('/maps')) {
policyConfig = { ...policyConfig, ...permissionsPolicies.maps };
} else if (req.path.startsWith('/checkout')) {
policyConfig = { ...policyConfig, ...permissionsPolicies.checkout };
}
res.setHeader('Permissions-Policy', buildPermissionsPolicy(policyConfig));
next();
});
Feature Detection and Progressive Enhancement
// Client-side feature detection with policy awareness
class FeatureDetector {
async checkPermission(feature) {
// Map feature names to permission names
const permissionMap = {
'camera': 'camera',
'microphone': 'microphone',
'geolocation': 'geolocation',
'notifications': 'notifications',
'persistent-storage': 'persistent-storage'
};
const permissionName = permissionMap[feature];
if (!permissionName || !navigator.permissions) {
return 'unsupported';
}
try {
const result = await navigator.permissions.query({ name: permissionName });
return result.state; // 'granted', 'denied', or 'prompt'
} catch (error) {
console.error(`Permission query failed for ${feature}:`, error);
return 'error';
}
}
async enableFeature(feature, enableCallback, fallbackCallback) {
const permission = await this.checkPermission(feature);
if (permission === 'granted') {
enableCallback();
} else if (permission === 'prompt') {
// Request permission through user action
const button = document.querySelector(`[data-feature="${feature}"]`);
button?.addEventListener('click', async () => {
try {
await enableCallback();
} catch (error) {
fallbackCallback(error);
}
});
} else {
fallbackCallback(new Error(`${feature} permission denied`));
}
}
}
// Usage example
const detector = new FeatureDetector();
// Check camera permission
detector.enableFeature(
'camera',
async () => {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
// Use camera stream
},
(error) => {
console.log('Camera not available:', error);
// Show alternative UI
}
);