Compare commits
7 Commits
9.3.0-alph
...
alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b93c618bc2 | ||
| ce5dde808a | |||
| 78b803abe7 | |||
| ef1d5f44e7 | |||
| ca873bc238 | |||
| c0ef385a7b | |||
|
|
d186471d45 |
17
package-lock.json
generated
17
package-lock.json
generated
@@ -81,7 +81,7 @@
|
|||||||
"deep-diff": "1.0.2",
|
"deep-diff": "1.0.2",
|
||||||
"eslint": "9.27.0",
|
"eslint": "9.27.0",
|
||||||
"eslint-plugin-expect-type": "0.6.2",
|
"eslint-plugin-expect-type": "0.6.2",
|
||||||
"eslint-plugin-unused-imports": "4.3.0",
|
"eslint-plugin-unused-imports": "4.4.1",
|
||||||
"form-data": "4.0.5",
|
"form-data": "4.0.5",
|
||||||
"globals": "16.2.0",
|
"globals": "16.2.0",
|
||||||
"graphql-tag": "2.12.6",
|
"graphql-tag": "2.12.6",
|
||||||
@@ -9916,14 +9916,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint-plugin-unused-imports": {
|
"node_modules/eslint-plugin-unused-imports": {
|
||||||
"version": "4.3.0",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz",
|
||||||
"integrity": "sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==",
|
"integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
|
||||||
"eslint": "^9.0.0 || ^8.0.0"
|
"eslint": "^10.0.0 || ^9.0.0 || ^8.0.0"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@typescript-eslint/eslint-plugin": {
|
"@typescript-eslint/eslint-plugin": {
|
||||||
@@ -29458,9 +29457,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"eslint-plugin-unused-imports": {
|
"eslint-plugin-unused-imports": {
|
||||||
"version": "4.3.0",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.4.1.tgz",
|
||||||
"integrity": "sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==",
|
"integrity": "sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -88,7 +88,7 @@
|
|||||||
"deep-diff": "1.0.2",
|
"deep-diff": "1.0.2",
|
||||||
"eslint": "9.27.0",
|
"eslint": "9.27.0",
|
||||||
"eslint-plugin-expect-type": "0.6.2",
|
"eslint-plugin-expect-type": "0.6.2",
|
||||||
"eslint-plugin-unused-imports": "4.3.0",
|
"eslint-plugin-unused-imports": "4.4.1",
|
||||||
"form-data": "4.0.5",
|
"form-data": "4.0.5",
|
||||||
"globals": "16.2.0",
|
"globals": "16.2.0",
|
||||||
"graphql-tag": "2.12.6",
|
"graphql-tag": "2.12.6",
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ import linkedin from './linkedin';
|
|||||||
const meetup = require('./meetup');
|
const meetup = require('./meetup');
|
||||||
import mfa from './mfa';
|
import mfa from './mfa';
|
||||||
import microsoft from './microsoft';
|
import microsoft from './microsoft';
|
||||||
|
const nintendo = require("./nintendo");
|
||||||
import oauth2 from './oauth2';
|
import oauth2 from './oauth2';
|
||||||
const phantauth = require('./phantauth');
|
const phantauth = require('./phantauth');
|
||||||
import qq from './qq';
|
import qq from './qq';
|
||||||
import spotify from './spotify';
|
import spotify from './spotify';
|
||||||
|
const steam = require("./steam");
|
||||||
import twitter from './twitter';
|
import twitter from './twitter';
|
||||||
const vkontakte = require('./vkontakte');
|
const vkontakte = require('./vkontakte');
|
||||||
import wechat from './wechat';
|
import wechat from './wechat';
|
||||||
@@ -47,9 +49,11 @@ const providers = {
|
|||||||
linkedin,
|
linkedin,
|
||||||
meetup,
|
meetup,
|
||||||
mfa,
|
mfa,
|
||||||
|
nintendo,
|
||||||
google,
|
google,
|
||||||
github,
|
github,
|
||||||
twitter,
|
twitter,
|
||||||
|
steam,
|
||||||
spotify,
|
spotify,
|
||||||
anonymous,
|
anonymous,
|
||||||
digits,
|
digits,
|
||||||
|
|||||||
99
src/Adapters/Auth/nintendo.js
Normal file
99
src/Adapters/Auth/nintendo.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
var Parse = require('parse/node').Parse;
|
||||||
|
const { URL } = require('url');
|
||||||
|
var jwt = require('jsonwebtoken');
|
||||||
|
var jwksClient = require('jwks-rsa');
|
||||||
|
|
||||||
|
// Returns a promise that fulfills iff this nsa id token is valid
|
||||||
|
function validateAuthData(authData, authOptions) {
|
||||||
|
//console.log("going to validate for nintendo");
|
||||||
|
//console.log(authData);
|
||||||
|
if ("token" in authData) {
|
||||||
|
try {
|
||||||
|
var token = authData["token"];
|
||||||
|
var decoded = jwt.decode(token, {complete: true});
|
||||||
|
var header = decoded.header;
|
||||||
|
|
||||||
|
// console.log("got nsa id token, header is:");
|
||||||
|
// console.log(header);
|
||||||
|
// console.log("full decoded token is:");
|
||||||
|
// console.log(decoded);
|
||||||
|
|
||||||
|
if (!('alg' in header) || header['alg'] != "RS256") {
|
||||||
|
error("No algorithm specified or it didn't match expected value 'RS256'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('kid' in header) || !('jku' in header)) {
|
||||||
|
error("Either 'kid' or 'jku' value not present in token.");
|
||||||
|
}
|
||||||
|
var jwk_name = header['kid'];
|
||||||
|
var jku = header['jku'];
|
||||||
|
|
||||||
|
if (!isValidJKU(jku)) {
|
||||||
|
error("JKU url in token isn't valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
var client = jwksClient({
|
||||||
|
jwksUri: jku
|
||||||
|
});
|
||||||
|
function getKey(header, callback) {
|
||||||
|
client.getSigningKey(header.kid, function (err, key) {
|
||||||
|
var signingKey = key.publicKey || key.rsaPublicKey;
|
||||||
|
callback(null, signingKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var options = {};
|
||||||
|
jwt.verify(token, getKey, options, function(err, decoded) {
|
||||||
|
// console.log("verfied jwt, decoded value is:");
|
||||||
|
// console.log(decoded);
|
||||||
|
if (err != null) {
|
||||||
|
reject("Error verifying jwt: " + err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!new URL(decoded.iss).hostname.endsWith("nintendo.com")) {
|
||||||
|
reject("iss claim in token is not a nintendo server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var now = Math.floor(Date.now() / 1000);
|
||||||
|
if (Number.parseInt(decoded.iat) > (now + 10000)) {
|
||||||
|
reject("iat value is not in the past");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Number.parseInt(decoded.exp) < (now - 10000)) {
|
||||||
|
reject("exp value is not in the future");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (decoded.nintendo.ai != authOptions.serverId) {
|
||||||
|
reject("application id does not match our id");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(decoded);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
error('Error authenticating NSA id token: ' + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
error('No token found in the request');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// steam auth bundles the app id in the auth data so don't validate seperately
|
||||||
|
function validateAppId() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidJKU(jku) {
|
||||||
|
// todo - validate this properly?
|
||||||
|
return new URL(jku).hostname.endsWith("nintendo.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
function error(message) {
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, message);
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
validateAppId,
|
||||||
|
validateAuthData
|
||||||
|
};
|
||||||
92
src/Adapters/Auth/steam.js
Normal file
92
src/Adapters/Auth/steam.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* Parse Server authentication adapter for Steam.
|
||||||
|
*
|
||||||
|
* @class SteamAdapter
|
||||||
|
* @param {Object} options - The adapter configuration options.
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* ## Parse Server Configuration
|
||||||
|
* To configure Parse Server for Steam authentication, use the following structure:
|
||||||
|
* ```json
|
||||||
|
* {
|
||||||
|
* "auth": {
|
||||||
|
* "steam": {
|
||||||
|
* "appId": "your-app-id",
|
||||||
|
* "webApiKey": "your-web-api-key"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The adapter requires the following `authData` fields:
|
||||||
|
*
|
||||||
|
* ## Auth Payloads
|
||||||
|
* ```json
|
||||||
|
* {
|
||||||
|
* "steam": {
|
||||||
|
* "??": "??"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @see {@link https://partner.steamgames.com/doc/api/ISteamUser#GetAuthTicketForWebApi Steam Web API docs}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var Parse = require('parse/node').Parse;
|
||||||
|
const https = require('https');
|
||||||
|
const querystring = require('querystring');
|
||||||
|
|
||||||
|
|
||||||
|
// Returns a promise that fulfills iff this application ticket is valid
|
||||||
|
function validateAuthData(authData, authOptions) {
|
||||||
|
if ("auth_ticket" in authData) {
|
||||||
|
//console.log("Authenticate steam user using web api and auth ticket");
|
||||||
|
return callSteamWebApi(authData.auth_ticket, authOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// steam auth bundles the app id in the auth data so don't validate seperately
|
||||||
|
function validateAppId() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
function callSteamWebApi(auth_ticket, authOptions) {
|
||||||
|
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
// GET parameters
|
||||||
|
const parameters = {
|
||||||
|
key: authOptions.webApiKey,
|
||||||
|
appid: authOptions.appId, // could try the demo id too, but we know that doesn't allow online play so don't worry for now
|
||||||
|
ticket: auth_ticket,
|
||||||
|
identity: authOptions.serverId
|
||||||
|
}
|
||||||
|
|
||||||
|
const get_request_args = querystring.stringify(parameters);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
host: "partner.steam-api.com",
|
||||||
|
path: "/ISteamUserAuth/AuthenticateUserTicket/v1/?" + get_request_args,
|
||||||
|
headers : {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = https.request(options, (response) => {
|
||||||
|
//console.log("Steam web auth sucess");
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
request.on('error', (error) => {
|
||||||
|
//console.log(error.message);
|
||||||
|
reject('The Steam web api could not authenticate the user with the given auth ticket');
|
||||||
|
});
|
||||||
|
|
||||||
|
request.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
validateAppId,
|
||||||
|
validateAuthData
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user