We need to implement SAML based secure authentication. Our IDP will be Okta and OneLogin. For this we use "passport" + "passport-saml" in our node.js express app. We use the following strategy configuration at SP side for Okta-
var oktaLoginStrategy = {
host: 'http://localhost:3000',
path: '/login/callback',
realm: 'urn:node:app',
entryPoint: "https://dev-528399.oktapreview.com/app/builtiodev528399_oktasp1_1/exkbbi8vwj2OsHjbE0h7/sso/saml",
issuer: "http://www.okta.com/exkbbi8vwj2OsHjbE0h7",
additionalParams: {
'RelayState': "test"
},
signatureAlgorithm: 'sha256',
decryptionPvk: privateKey,
privateCert: privateKey,
cert: oktaPublicKey,
identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
};
and for OneLogin we use-
var oneLoginStrategy = {
host: 'http://localhost:3000',
path: '/login/callback',
realm: 'urn:node:app',
entryPoint: "https://flow-dev.onelogin.com/trust/saml2/http-post/sso/686218",
issuer: "https://app.onelogin.com/saml/metadata/686218",
additionalParams: {
'RelayState': "test"
},
signatureAlgorithm: 'sha256',
decryptionPvk: privateKey,
privateCert: privateKey,
cert: oneLoginPublicKey,
validateInResponseTo: true,
identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'
};
But for Okta it is giving error as "Cannot read property 'getAttribute' of undefined" and for OneLogin it is giving error as "Invalid signature". Further, we debugged into the module and found that for Okta it breaks while finding algorithm and for OneLogin, it able to decrypt 'CipherData' but it breaks while validating the signature.
Our private key format is-
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
Please help us into this.
Here the issue is from Okta SAML assertion format, we have made changes in "decryptKeyInfo" function of xml-encryption module which is used under passport-saml to find "keyEncryptionMethod" from assertion. About OneLogin it was our setup issue.
Related
I tried to get the access token and refresh token in the /token endpoint. I got authorization code and I will pass the token endpoint but it's throwing grant request is invalid error. How to fix this issue
Configuration
const oidc = new Provider('http://localhost:3000', {
clients: [
{
// client_id: 'foo',
// redirect_uris: ['https://jwt.io'], // using jwt.io as redirect_uri to show the ID Token contents
// response_types: ['id_token'],
// grant_types: ['implicit'],
// token_endpoint_auth_method: 'none',
client_id: 'secret',
redirect_uris: ['http://localhost:3000/api/v1'], // using jwt.io as redirect_uri to show the ID Token contents
response_types: ['code'],
grant_types: ['authorization_code', 'refresh_token'],
token_endpoint_auth_method: 'none',
},
],
cookies: {
keys: 'secret key',
},
features: {
clientCredentials: {enable:true},
introspection: {enable:true}
},
pkce: {
required: true
},
token_endpoint_auth_method: "none",
});
Token API
How to fix this Issue. I tried many ways and referred many documentation but I could not get the solution
Your access token request is missing the PKCE code_verifier parameter.
you can start your provider process with DEBUG=oidc-provider:* to get more details for these errors.
I'm using Axios in ReactJS to call my API that is hosted on the cloud with a self-signed certificate. The error for the request returns net::ERR_CERT_REVOKED.
I've added the self-signed code to my login keychain on MacOS running reactjs. But the cert is still getting revoked when I view the error on the logs on Chrome. On safari, the error is Failed to load resource: The certificate for this server is invalid.
try {
const response = await axios.post(
'https://1.1.1.1:3000/login',
{ withCredentials: true },
{ auth: apiAuth },
{ data: bodyFormData },
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
)
return response.data;
} catch (error) {
console.log(error);
}
IP address has been changed for the question.
I've tried to use the following code in the Axios request but it doesn't help
const agent = new https.Agent({
rejectUnauthorized: false
});
Expected the server to give a response but getting cert revoked as response.
self-signed certificate ... net::ERR_CERT_REVOKED ... MacOS
You probably run into the new requirements for certificates in MacOS 10.15 and iOS 13 which seem to be enforced also for self-signed certificates. While you don't provide any details about your specific certificate I guess it is valid for more than 825 days. It might of course also be any other of the new requirements - see Requirements for trusted certificates in iOS 13 and macOS 10.15 for the details.
i get a certificate from an azure key vault :
const certificate = await keyVaultClient.getCertificate(this.keyVaultUri, certificateName, '');
i get the certificate ... first good thing :)
But after i don't find how i can set the certificate in the header of an https request.
i've tried different ways without any success.
Someone knows how i can achieve that. Below test done.
var options = {
hostname: 'url.domain.com',
port: 443,
path: '/method1',
method: 'GET',
agent: false,
rejectUnauthorized: false,
strictSSL: false,
//pfx: certificate.cer.toString('utf8'),
//pfx: certificate.cer.toString('base64'),
pfx: certificate,
passphrase: 'passphrase'
};
const req = https.request(options, (res: any) => { });
Note that if i use the certificate on the drive, it works :
pfx: fs.readFileSync(__dirname + '/my.pfx'),
Thanks in advance
i've found a solution.
First to get the certificate, i've used the method 'getSercret' in place of 'getCertificate' :
let keyVaultClient = new KeyVault.KeyVaultClient(azureCredential);
const secret = await keyVaultClient.getSecret(this.keyVaultUri, secretName, '');
In the header option, i've passed the secret in a buffer with base 64 format :
var options = {
hostname: 'myurl.ti',
port: 443,
path: '/mySuperMethod',
method: 'GET',
agent: false,
rejectUnauthorized: false,
strictSSL: false,
pfx: new Buffer(secret.value, 'base64'),
passphrase: ''
};
You see that the passphrase (password to secure password) is empty. Why ? in fact when you upload a certificate in azure keyvault, you have to mention the password. But after keyvault doesn't keep the password and it's set to blank.
That's known by microsoft and for me it's an issue.
Here an article that explain that : https://thuansoldier.net/7462/
Regards Mathieu
** Disclaimer -- I'm new to the world of oAuth and OpenIDConnect -- please be patient if I'm asking a stupid question here.
I want to create a SPA that will request data from an API. Both the SPA and API are hosted on the same nodejs server. I want anyone accessing data and/or the app to be authenticated with our AzureAD tenant on Office365.
Currently, I have the authentication piece working using passport-azure-ad.OIDCStrategy. However, in my app, I would also like to be able to access information from the Microsoft GRAPH api in the server side api code. However, the OIDC connection that I've already made does not seem to be enough to allow me access to the GRAPH api. It appears that maybe I need a jwt bearer token.
My question is, do I need to use the access token from the OIDC response to get a bearer token? If so, how do I go about this (on the server side -- nodejs)?
I tried viewing the example listed in passport-auth-ad for BearerStrategy v2 endpoint. What confuses me though is that it uses OIDCStrategy! Does that also return a bearer token? If so, am I already receiving everything I need in my first OIDCStrategy call?
Thanks for whatever help you can offer!
Update
https.request({
hostname: "graph.microsoft.com",
path: '/v1.0/me/messages',
port: 443,
method: 'GET',
headers: {Authorization: 'Bearer ' + req.user.token, Accept: "application/json"}
},(rs) => {
console.log("HTTPS Response Status: ", rs.statusCode);
console.log("HTTPS Response Headers: ", rs.headers)
rs.on('data', (d) => {
res.send(d)
})
}).end();
Error Message:
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure.", ...
I confirmed that the token is the same token that was passed as the id_token in the auth callback from Azure. Any thoughts?
Update 2
A few more code snippets to help in diagnosing where I may be going wrong.
Strategy Config
//Still test code so user management not fully implemented
passport.use("azure", new azureStrategy({
identityMetadata: 'https://login.microsoftonline.com/common/.well-known/openid-configuration',
clientID: "*********************",
responseType: 'code id_token',
issuer: "https://sts.windows.net/****************/",
responseMode: 'form_post',
redirectUrl: "https://localhost:5070/auth/azure/callback",
allowHttpForRedirectUrl: true,
clientSecret: "***************" ,
state: "************"
},
(iss, sub, profile, claims, accessToken, refreshToken, params, done) => {
process.nextTick(() => {
var user = usvc.findUserByAltId(profile.oid, "azure");
if(!user){
}
})
done(null, {id: profile.oid, name: profile.displayName, email: profile.upn, photoURL: "", token: params.id_token });
}));
Route Definitions
app.get("/auth/azure", azure.passport.authenticate(
'azure', {scope: ['Mail.Read','User.Read'], failureRedirect: '/'}))
app.post("/auth/azure/callback", azure.passport.authenticate(
"azure", {scope: ['Mail.Read','User.Read'], failureRedirect: "/error.html"}),
(req, res) => {res.redirect("/user")})
The OpenIDConnect work grand flow also will returns a JWT token for Authentication & Authorization. You can use the id_token in Authentication header for the resources. However, some operations in Graph APIs require an administrator permission.
You can try to run the following script in PowerShell to upgrade your Azure AD application's privilege.
Connect-MsolService
$ClientIdWebApp = '{your_AD_application_client_id}'
$webApp = Get-MsolServicePrincipal –AppPrincipalId $ClientIdWebApp
#use Add-MsolRoleMember to add it to "Company Administrator" role).
Add-MsolRoleMember -RoleName "Company Administrator" -RoleMemberType ServicePrincipal -RoleMemberObjectId $webApp.ObjectId
I'm using request in my app to send a POST request over HTTPS with Client Authentication. Request always throws an error Error: Invalid URI "/" and I couldn't do anything to solve it. I've tried used url.parse instead of passing a string but it's still the same.
request.post({
uri: 'https://localhost:5000',
key: credentials.key,
ca: credentials.ca,
cert: credentials.cert,
passphrase: credentials.passphrase,
rejectUnauthorized: false
}, { form: { data: payload }});
Turns out it was caused by passing the second object to request.post, it should be inside the first object.
request.post('https://localhost:5000/', {
key: credentials.key,
ca: credentials.ca,
cert: credentials.cert,
passphrase: credentials.passphrase,
rejectUnauthorized: false,
form: { data: payload }
});