The authentication example found in the apollo docs is as follows:
https://github.com/apollostack/core-docs/blob/master/source/network.md#afterware
```
networkInterface.useAfter([{
applyAfterware({ response }, next) {
if (response.status === 401) {
logout();
}
next();
}
}]);
```
But what does the logout function look like? I'm unclear how to actually dispatch this as an action if it is indeed an action. The docs are really unclear on this and I haven't been able to find any example code anywhere. Any thoughts?
Look at my implementation:
const withUserHandlers = withHandlers({
logout: props => () => {
const { client, navigation, setUser } = props; // eslint-disable-line
removeTokenFromUse(client);
client.resetStore();
setUser(null);
const resetAction = NavigationActions.reset({
index: 0,
actions: [ NavigationActions.navigate({ routeName: 'Login' }) ],
});
navigation.dispatch(resetAction);
}
});
Related
Here is what I tried but it's not working.
This guy is responsible for creating a composition after the meeting is ended.
app.post('/api/endMeeting', (req, res) => {
const roomSid = req.body.roomSid;
userEmail = req.body.userEmail;
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
client.video.rooms(roomSid).update({ status: 'completed' });
client.video.compositions.create({
roomSid: roomSid,
audioSources: '*',
videoLayout: {
grid : {
video_sources: ['*']
}
},
statusCallback: `${process.env.REACT_APP_BASE_URL}/api/getMeeting`,
statusCallbackMethod: 'POST',
format: 'mp4'
}).then(() => {
// sendRecordingEmail(composition.sid, userEmail);
res.status(200).send({
message: 'success'
});
}).catch(err => {
res.status(500).send({
message: err.message
});
});
});
And this guy will send the download link of the composition to the participant when it's available.
app.post('/api/getMeeting', (req, res) => {
if (req.query.StatusCallbackEvent === 'composition-available') {
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
const compositionSid = req.query.CompositionSid;
const uri = "https://video.twilio.com/v1/Compositions/" + compositionSid + "/Media?Ttl=3600";
client.request({
method: "GET",
uri: uri,
}).then((response) => {
const requestUrl = request(response.data.redirect_to);
sendRecordingEmail(requestUrl, userEmail);
res.status(200).send("success");
}).catch((error) => {
res.status(500).send("Error fetching /Media resource " + error);
});
}
});
I can confirm that the composition is created exactly in the Twilio console.
But it seems the status callback guy is not working and I can see the below issue.
It seems I made mistakes in using the status callback.
Please let me know what is the problem and how I can solve this.
Thank you.
Thank you very much for #philnash's help in solving this problem.👍
I solved the above issue and I can get the download link of the composition for now.
The problem was in the status callback function and I should use req.body instead of req.query because of the status callback method. (It's POST on my code.)
Here is the code that is fixed.
app.post('/api/getMeeting', (req, res) => {
if (req.body.StatusCallbackEvent === 'composition-available') {
const client = require('twilio')(config.twilio.apiKey, config.twilio.apiSecret, {accountSid: config.twilio.accountSid});
const compositionSid = req.body.CompositionSid;
const uri = "https://video.twilio.com/v1/Compositions/" + compositionSid + "/Media?Ttl=3600";
client.request({
method: "GET",
uri: uri,
}).then((response) => {
const requestUrl = response.body.redirect_to; // Getting the redirect link that user can download composition
sendRecordingEmail(requestUrl, userEmail); // Send URL via email to the user
res.status(200).send("success");
}).catch((error) => {
res.status(500).send("Error fetching /Media resource " + error);
});
} else {
res.status(204).send('compositioin is not available');
}
});
I just wanted to setup a simple custom middleware in strapi. I have tried what they are writing in docs but I found that environments folder and inside configurations are removed. Follwing that currently I have writtent.
/config/environments/development/middleware.json
{
"subscribers": {
"enabled": true
}
}
/config/middleware.json
{
"timeout": 100,
"load": {
"before": ["responseTime", "logger", "cors", "responses", "gzip"],
"order": ["parser", "subscribers"],
"after": ["router"]
}
}
/middlewares/subscribers/index.js
module.exports = (strapi) => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
console.log("I have been called!");
await next();
});
},
};
};
Please help me to implement a middleware in strapi api.Thanks beforehand.
I just did what is written in the docs and I will do the same in my answer!
Initially I was reading from an older version of documentation which is mentioned by #Derrick Mehaffy. I found the correct docs url and read through its middleware implementation. [LINK TO THE DOCS] (Below explanations are obtained from docs)
------------------------------------------------------------------------------------
Examples: Create your custom middleware. [Path — ./middlewares/timer/index.js]
module.exports = strapi => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
const start = Date.now();
// I just add custom code that logs `I have been called!`
console.log('I have been called!');
await next();
const delta = Math.ceil(Date.now() - start);
ctx.set('X-Response-Time', delta + 'ms');
});
},
};
};
Enable the middleware in environments settings.
Load a middleware at the very first place - !You can do at the proper order
Path — ./config/middleware.js
module.exports = {
load: {
before: ["timer", "responseTime", "logger", "cors", "responses", "gzip"],
order: ["parser", ],
after: ["router", ],
},
settings: {
timer: {
enabled: true,
},
},
};
Basically I just copied and pasted the answer from docs, but it might be helpful for future use that's I have left the question
Everything seems to be right and the files are being cached but it just doesn't work offline. Am I missing something obvious?
the cache.addAll did not want to work with my const FILES_TO_CACHE but do work when I put them in directly. Thus the repeated code.
Here is my service worker file:
const FILES_TO_CACHE = [
"/",
"/index.html",
"/style.css",
"/db.js",
"/index.js",
"/manifest.webmanifest"
];
const CACHE_NAME = "static-cache-v2";
const DATA_CACHE_NAME = "data-cache-v1";
// install
self.addEventListener("install", function(evt) {
evt.waitUntil(
caches.open(CACHE_NAME).then(cache => {
console.log("Your files were pre-cached successfully!");
return cache.addAll([
"/",
"/index.html",
"/style.css",
"/db.js",
"/index.js",
"/manifest.webmanifest"
]);
})
);
self.skipWaiting();
});
// activate
self.addEventListener("activate", function(evt) {
console.log("activated");
evt.waitUntil(
caches.keys().then(keyList => {
return Promise.all(
keyList.map(key => {
if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {
console.log("Removing old cache data", key);
return caches.delete(key);
}
})
).catch(err => console.log(err));
})
);
self.clients.claim();
});
// fetch
self.addEventListener("fetch", function(evt) {
console.log("fetched", evt.request.url);
if (evt.request.url.includes("/api/")) {
evt.respondWith(
caches
.open(FILES_TO_CACHE)
.then(cache => {
return fetch(evt.request)
.then(response => {
// If the response was good, clone it and store it in the cache.
if (response.status === 200) {
cache.put(evt.request.url, response.clone());
}
return response;
})
.catch(err => {
// Network request failed, try to get it from the cache.
return cache.match(evt.request);
});
})
.catch(err => console.log(err))
);
return;
}
});
link in html:
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/service-worker.js").then(function() {
console.log("Service Worker Registered");
});
}
</script>
I also have my manifest linked in the HTML file.
Thank you in advance for any help you can provide!
If you look at the last line of code here:
// fetch
self.addEventListener("fetch", function(evt) {
console.log("fetched", evt.request.url);
if (evt.request.url.includes("/api/")) {
you see that there's a very simple mistake – your Service Worker is ONLY responding to requests that start with "/api/". If they don't, the SW doesn't touch them. Thus only "/api/" calls work offline (which doesn't make any sense :-), apis being mostly dynamic, right?).
(It is possible that there's another bug in the code of course, but this is a good point to start making changes.)
I've got a Web API which I've secured with OAuth2 but I'm having difficulty getting Swagger UI to show the authentication option.
Currently, the api_key section of UI still shows, despite there being no configuration for it.
Here's my SwaggerConfig
public class SwaggerConfig
{
public static void Register()
{
if (ConfigUtil.SSOSupported)
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
// Swashbuckle.Application.OAuth2SchemeBuilder
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.Schemes(new[] { "http", "https" });
c.SingleApiVersion("v1", "API Adapter");
c.PrettyPrint();
c.ApiKey(string.Empty);
c.OAuth2("oauth2")
.Description("Description here")
.Flow("implicit")
.AuthorizationUrl(ConfigUtil.SSOAuthority() + "/connect/authorize")
.Scopes(scopes =>
{
scopes.Add("api", "api");
});
c.IgnoreObsoleteProperties();
c.DescribeAllEnumsAsStrings();
})
.EnableSwaggerUi(c =>
{
c.EnableOAuth2Support("your-client-id", "your-client-secret-if-required", "your-realms", "your-app-name");
});
}
}
}
This section is included in page source after loading:
window.swashbuckleConfig = {
rootUrl: 'http://localhost:44390',
discoveryPaths: arrayFrom('swagger/docs/v1'),
booleanValues: arrayFrom('true|false'),
validatorUrl: stringOrNullFrom(''),
customScripts: arrayFrom(''),
docExpansion: 'none',
supportedSubmitMethods: arrayFrom('get|put|post|delete|options|head|patch'),
oAuth2Enabled: ('true' == 'true'),
oAuth2ClientId: 'your-client-id',
oAuth2ClientSecret: 'your-client-secret-if-required',
oAuth2Realm: 'your-realms',
oAuth2AppName: 'your-app-name',
oAuth2ScopeSeperator: ' ',
oAuth2AdditionalQueryStringParams: JSON.parse('{}'),
apiKeyName: 'api_key',
apiKeyIn: 'query'
};
And the iniOAuth function is called:
window.swaggerUi = new SwaggerUi({
url: swashbuckleConfig.rootUrl + "/" + swashbuckleConfig.discoveryPaths[0],
dom_id: "swagger-ui-container",
booleanValues: swashbuckleConfig.booleanValues,
supportedSubmitMethods: swashbuckleConfig.supportedSubmitMethods,
onComplete: function(swaggerApi, swaggerUi){
if (typeof initOAuth == "function" && swashbuckleConfig.oAuth2Enabled) {
initOAuth({
clientId: swashbuckleConfig.oAuth2ClientId,
clientSecret: swashbuckleConfig.oAuth2ClientSecret,
realm: swashbuckleConfig.oAuth2Realm,
appName: swashbuckleConfig.oAuth2AppName,
scopeSeparator: swashbuckleConfig.oAuth2ScopeSeperator,
additionalQueryStringParams: swashbuckleConfig.oAuth2AdditionalQueryStringParams
});
But I just get the api_key text box and no Authorize button like I'd expect.
The controllers and actions all display without an issue.
I'm sure it's something small, but I've been fiddling for a couple of days now and have run out of ideas.
I'm trying to implement OAuth2 authentication in Angular 2 ( Electron ) application.
I achieve that on the way with a popup that is called after user click on 'Sign In' button.
In popup user types their credentials and allows the access and on confirm code is returned and I'm able to catch redirect request which I can't do without popup.
Here is implementation that works:
return Observable.create((observer: Observer<any>) => {
let authWindow = new electron.remote.BrowserWindow({ show: false, webPreferences: {
nodeIntegration: false
} });
authWindow.maximize();
const authUrl = AUTHORIZATION_WITH_PROOF_KEY_URL
+ `?client_id=${CLIENT_ID}&response_type=code&scope=api_search&`
+ `redirect_uri=${REDIRECT_URL}&code_challenge=${challenge}&code_challenge_method=S256`;
if (this.clearStorage) {
authWindow.webContents.session.clearStorageData({}, () => {
this.clearStorage = false;
authWindow.loadURL(authUrl);
authWindow.show();
});
} else {
authWindow.loadURL(authUrl);
authWindow.show();
}
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
const code = this.getCode(newUrl, authWindow);
if (!code) {
this.clearStorage = true;
return;
}
this.requestToken({
grant_type: 'authorization_code',
code: code,
code_verifier: verifier,
redirect_uri: REDIRECT_URL
})
.subscribe((response: { access_token: string, refresh_token: string }) => {
observer.next(response);
});
});
// Reset the authWindow on close
authWindow.on('close', () => {
authWindow = null;
});
});
and as you can see in above code I'm creating new BrowserWindow with:
new electron.remote.BrowserWindow({ show: false, webPreferences: {
nodeIntegration: false
} });
and with that approach I'm able to catch up redirect request with a block of code that starts with:
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
....
}
but I'm not able to solve this without popup ( modal ).
Here is my attempt:
return Observable.create((observer: Observer<any>) => {
let authWindow = electron.remote.getCurrentWindow();
const authUrl = AUTHORIZATION_WITH_PROOF_KEY_URL
+ `?client_id=${CLIENT_ID}&response_type=code&scope=api_search&`
+ `redirect_uri=${REDIRECT_URL}&code_challenge=${challenge}&code_challenge_method=S256`;
if (this.clearStorage) {
authWindow.webContents.session.clearStorageData({}, () => {
this.clearStorage = false;
authWindow.loadURL(authUrl);
});
} else {
authWindow.loadURL(authUrl);
}
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
debugger;
// this is not called, I'm not able to catch up redirect request
});
// Reset the authWindow on close
authWindow.on('close', () => {
authWindow = null;
});
});
With my approach I get login screen from remote URL in a current window, but the problem is that I'm not able to catch redirect request with ('did-get-redirect-request') event.
I also tried with 'will-navigate' and many others.
Although I don't have a direct answer I thought I'd point you to Google's AppAuth-JS libraries, which cover OAuth based usage for Electron Apps.
My company have used AppAuth libraries for the mobile case and they worked very well for us, so that we wrote less security code ourselves and avoided vulnerabilities.
There is also an Electron Code Sample.