Docebo Api Integration - api

I am facing an issue with implementing the API to authorize the user and make a session and launch a course.
All the full steps like making a token and passing it with REST API has been done and in the response I am receiving the success in the response token.
Now the issue is that when I am trying opening a course link, it redirects me to the login page despite landing on course. Can you please help set up a session and let me know which API is to be used to make a session so that it doesn't redirects me to the login page.

For those still looking for an answer, I'll show you how to generate a temporary link that will authorize a user and direct them to the desired location in Docebo.
Things you need:
The username.
The SSO secret for the token hash.
-In Docebo: Click APPS and Features on left-hand side. Click Third party integrations. Activate API and SSO, if not already activated. After API and SSO is active, click on its gear icon. Click the check box that starts with "Enable SSO with...". Enter a SSO secret in the box below the checkbox. Save.
Now, for the implementation. I myself used C# for this but hopefully it will be easily translatable to your language of choice (or lack of choice).
The basic idea is this:
1) Create an MD5 hash of three values:
(NOTE: Include commas between the values when generating the hash. Example further below...)
username(lowercase!!!)
time = Seconds since the Unix Epoch in utc.
SSO secret (the one you typed yourself).
2) Get the hex value of the hash.
3) Combine the destination url with the username, time, and hex. Like so:
http[s]://[yourdomain]/lms/index.php?r=site/sso&login_user=[username]&time=[utc
time]&token=[token]{&id_course=[id_course]}{&destination=[destination]}
For my example, I didn't specify a course or a destination.
Here is the above gibberish, in C#:
public string GenerateDoceboSSOLink()
{
string userName = "johnsmith"; //Note the lowercase!!
string domain = "http://my-test.docebosaas.com";
string ssoSecret = "MySSOSecret";
//Getting the seconds since the Unix Epoch
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
int time = (int)t.TotalSeconds;
//Creating the hash...
MD5 md5 = System.Security.Cryptography.MD5.Create();
//Note the inclusion of the commas!
string input = userName + "," + time + "," + ssoSecret;
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hash = md5.ComputeHash(inputBytes);
//Getting the hex value of the hash.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
string token = sb.ToString(); //the hex value, which we will call token
//The sso link.
string link = String.Format("{0}/lms/index.php?r=site/sso&login_user={1}&time={2}&token={3}", domain, userName, time, token);
return link;
}
So, I followed this
impossible-to-find documentation that led me to what you see above (I couldn't find the url so I just shared it).

Related

Authenticate in Xero from Salesforce

I'm new to Oauth and I stack on getting oauth_access_token to work with Xero. Web Service authentication doesn't work for me.
Xero returns the following error message "oauth_problem=signature_invalid&oauth_problem_advice=Failed to validate signature".
The generated signature is incorrect, but what is right way to generate it?
Here is APEX code which generates Endpoint. What is wrong?
Http h = new Http();
String consumer_key='XXX';
Long tmp=(System.now().getTime()/1000);
Blob isItCorrect = Crypto.generateMac('HMacSHA1', Blob.valueOf('https://api.xero.com/api.xro/2.0'), Blob.valueOf(consumer_key));
String signature= EncodingUtil.urlEncode(EncodingUtil.base64Encode(isItCorrect), 'UTF-8');
// Try to get access token
HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.xero.com/oauth/RequestToken?oauth_consumer_key='+consumer_key+
'&oauth_signature_method=RSA-SHA1'+
'&oauth_signature='+signature+
'&oauth_timestamp='+tmp+ '&oauth_nonce='+tmp+'&oauth_version=1.0&scope=https%3A%2F%2Fapi.xero.com%2Fapi.xro%2F2.0');
req.setMethod('GET');
// Send the request, and return a response
HttpResponse res = h.send(req);
System.debug('~~~ '+res.getBody());
It generates following Endpoint:
Endpoint=https://api.xero.com/oauth/RequestToken?oauth_consumer_key=ICSP7Y5K2TG7RIIC6Y7R7KLC1AHWYC&oauth_signature_method=RSA-SHA1&oauth_signature=gWP02y2EIatw4xilTvd5Iq3e0%2Fw%3D&oauth_timestamp=1372123781&oauth_nonce=1372123781&oauth_version=1.0&scope=https%3A%2F%2Fapi.xero.com%2Fapi.xro%2F2.0
Just as an aside: I've never worked with salesforce so I'm not sure if there's a better
way to leverage existing oauth work on the platform, it's very rare
now to have to write all the oauth signature stuff yourself and it's
easy to make a mistake but here goes]
I think your signature base string is incorrect.
As far as I can tell you're just performing HMAC-SHA1 over https://api.xero.com/api.xro/2.0
if you read the OAuth Spec here: http://oauth.net/core/1.0/#anchor14 you need to construct the following base string (based on the request above)
GET&https%3A%2F%2Fapi.xero.com%2Foauth%2Frequesttoken&oauth_consumer_key%3DCONSUMER_KEY%26oauth_nonce (etc etc, just append all your query parameters apart from oauth_consumer as url encoded key=value pairs, in alphabetical order)
and then you need to create the hash with the key CONSUMER_KEY&CONSUMER_SECRET (both CONSUMER_KEY and CONSUMER_SECRET should be parameter encoded as per the OAuth Spec)
That should give you a valid signature..
Edit: I found this library which might be of help: https://code.google.com/p/sfdc-oauth-playground/

Understanding how rampart engine works

I know that Apache Rampart configuration allows providing a password callback handler class, that can be used to provide passwords needed for Rampart engine to build username tokens and create signatures when sending messages.
It's written that Whenever Rampart Engine needs a password to create a username token, it will create a WSPasswordCallback instance setting the appropriate identifier which it extracts from the parameter of the Rampart configuration and pass it to the password callback class via the handle method. But as you see I've used policy based configuration!
SO I've got a few questions to see if I have understand all all that:
Is i from here where rampart engine extracts the appropriate username - wsse:Username>bob</wsse:Username>'+
After it extracts it it passes it to our PWCBHandler class via handle method.
Our handle method sets the appropriate password if the username is correct.
And the most important - as I have to consume my web service from javascript at the end I have provided my soap request. But as you see I provide both the username and the password and I can't see where is security as everyone can see my username and password. Is this right. How can I make it more secure.
here is my code.
Here is my code for PassWordCallback.java class
ublic void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];
if(pwcb.getIdentifier().equals("test") &&pwcb.getPassword().equals("pass")) {
return;
}
else {
throw new UnsupportedCallbackException(callbacks[i],"Incorrect login/password");
}
}
}
here is my soaprequest from javascript
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soapenv:Envelope " +
"xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
"xmlns:tan=\"http://tan\">"+
"<soapenv:Header>"+
'<wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" soapenv:mustUnderstand="1">'+
'<wsse:UsernameToken xmlns:wsu="http://docs.oasisopen.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="123">'+
'<wsse:Username>bob</wsse:Username>'+
'<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">bobPW</wsse:Password>'+
'</wsse:UsernameToken>'+
'</wsse:Security>'+
"</soapenv:Header>"+
"<soapenv:Body>" +
"<tan:testws>" +
'<tan:x>ECHOO</tan:x>' +
' </tan:testws>'+
'</soapenv:Body>' +
'</soapenv:Envelope>';
I'll answer your 3rd question first
You are using username token authentication method to authenticate the service. To provide security you need to use https transport instead of http. this way you can provide transport level security and hide your password. Some nice reading can be found here http://wso2.com/library/3190/
If you are using insecure channel (say http) then you can encrypt the password. user can create a digest of the password with a random bytes (nounce).To authenticate the request the service will compute the digest value using the password bound to the received usename and will compare the received digest value and the computed digest value. the security headers will be changed. some detailed info can be found in this about this. http://wso2.com/library/240/ . to provide more security you can encrypt the message (http://wso2.com/library/3415/)
For question 1, it does not get the user name from wsse:Username. it extracts the username from the parameter of the Rampart configuration. This configuration can be loaded externally or using java
RampartConfig rc = new RampartConfig();
rc.setUser("admin");
rc.setPwCbClass(PWDCallBackHandler.class.getName());
some info regarding callback handler http://wso2.com/library/3733/
for question 2: yes

Security for a REST based API

I have been looking at techniques to secure an API for use in an android/iphone app or website application.
I have found one technique which I like but am unsure about if it is a good or what is wrong with it (aside from being a pritty long process).
Processing (users side initially):
First a salt is created by hashing the users password.
Then a signature is created by hashing the requested url (with username appended on the end via a query string) and the salt.
Lastly a token is created by hashing the username and the signature.
The token is passed inside a header to the server (everytime).
First Request:
The first request must be for the validate endpoint and include the device_id as a query string.
The same processing (as above) is done on the server and if the token matches that sent from the user than the device_id is stored in the database and is assigned to that username for future reference (the device id is found in the requested url) and is used to verify the username/device thereafter.
All subsequent requests:
The processing must take place on the users end and servers end for every request now with the token being different everytime (as the requested url changes).
No code is included as it is not written yet.
Your authentication model is a shared secret authentication. In your case your user's password serves as the shared secret. You need to ensure you have a secure way for getting the password to the user and server ahead of time. In order to sign the request you create a message with all your request headers and data. Then hash that request. Then that hash (token) will be passed with the request. The server will perform the same signing and hashing process on the server and ensure the tokens match.
In your example your sound like you want to create the token with this pseudo code:
Token = hmac-sha1( Hash(Pasword + Salt) + RequestUrl + UserName )
Your way is not bad but I would compare your method to Amazon's REST Auth model and implement a closer version of what they have detailed. http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
Their implementation:
"Authorization: AWS " + AWSAccessKeyId + ":" + base64(hmac-sha1(VERB + "\n"
+ CONTENT-MD5 + "\n"
+ CONTENT-TYPE + "\n"
+ DATE + "\n"
+ CanonicalizedAmzHeaders + "\n"
+ CanonicalizedResource))
They have good reasons for including some fields that you have left out, including but not limited to:
The timestamp is to prevent replay attacks.
The content-MD5 is to prevent prevents people tampering with the request data (relevant to
POST and PUTS)

Testing WCF with SoapUI

I need your help on one practical issue. I have created a WCF service with basic binding with two operation contact.
1- void StartRegistration - Anonymous member can fill the basic registration form and press submit. All the information will be stored into the database and one link with some random token will be send to user's email address.
2 - void CompleteRegistration - This method validates the token sent into the email address and if token is valid, user account will be activated.
Now I have issue here. Using SoapUI I can call StartRegistration method. Email is sent to destination but I want to pass the token to CompleteRegistration method.
Since it is a WCF service so can not do dependency injection to pass the SoapUI tests :).
Please help.
If I understand your question correctly, you have two WCF methods, one for creating a token and another for confirming it.
What I would do in this case is have the first method, StartRegistration, return the token. Then you could use that token to pass into the CompleteRegistration method quite easily in Soap UI.
Another, quite messy solution, would be to have a groovy script test step in Soap UI that actually connected to the mail account, read the link and parsed the contents.
Edited:
Here is part of the script you'll need. Place it in a groovy step, that will then return the token from your mail.
Note: This code assumes that mail is plain text, not multipart. It also assumes that the mail box only has a single mail. The API for JavaMail is pretty extensive, so if you want to do any magic with it, Google is your friend :) At least, this is somewhere to start.
import javax.mail.*;
import javax.mail.internet.*;
// setup connection
Properties props = new Properties();
def host = "pop3.live.com";
def username = "mymailadress#live.com";
def password = "myPassword";
def provider = "pop3s";
// Connect to the POP3 server
Session session = Session.getDefaultInstance props, null
Store store = session.getStore provider
Folder inbox = null
String content
try
{
store.connect host, username, password
// Open the folder
inbox = store.getFolder 'INBOX'
if (!inbox) {
println 'No INBOX'
System.exit 1
}
inbox.open(Folder.READ_ONLY)
Message[] messages = inbox.getMessages()
content = messages[0].getContent()
//Do some parsing of the content here, to find your token.
//Place the result in content
}
finally
{
inbox.close false
store.close()
}
return content; //return the parsed token

How can I generate date-dependent passwords?

I'm looking for a simple way to generate passwords that will only work once for a limited amount of time, e.g. 1 day, 1 week, 1 month. This has to be implemented in an application that has no connectivity so a server isn't possible. The use case is something like:
1. Generate password for a specific date and length of time.
2. Send to user (email, phone, etc).
3. User enters in application.
4. Application is enabled for a specific time.
5. Password cannot be reused, even on another PC.
I'm assuming the only way to do this is to generate passwords that only work between a specific set of dates. Can anyone recommend an algorithm that can do this? It doesn't have to be incredibly secure, and I know you can crack this by resetting the time on the PC!
Thanks.
I know I'm late but I'll provide my advice anyway in case someone else who needs it found their way here.
To prevent it being used on another PC, you could probably use the MAC address or hardware address. However, this is subject to the network hardware being still available when checking the password. Please make sure you use the hardware address of the machine where the password will be checked.
private string GetBase64Mac()
{
System.Net.NetworkInformation.NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
if (interfaces.Length == 0)
{
System.Net.NetworkInformation.PhysicalAddress add = interfaces[0].GetPhysicalAddress();
if (add != null)
return System.Convert.ToBase64String(add.GetAddressBytes());
}
return "";
}
To limit it by some expiry date simply use the text string of the expiry date.
private string GetExpiryDate(DateTime expiryDate)
{
return expiryDate.ToString("yyyyMMdd");
}
Simply use a hash function to hash the combine expiry date, hardware address and a secret key. Prefix or suffix the hash output with the expiry date.
private void GeneratePassword(string prefix)
{
string secretKey = "MySecretKey";
System.Security.Cryptography.SHA1 sha = System.Security.Cryptography.SHA1.Create();
byte[] preHash = System.Text.Encoding.UTF32.GetBytes(prefix + secretKey + GetBase64Mac());
byte[] hash = sha.ComputeHash(preHash);
string password = prefix + System.Convert.ToBase64String(hash);
return password;
}
In the case above, i prefix the hash with the expiry date. So, when we check the password, we simply extract the expiry date from the password, use the same function to generate the same password. If the generated password match the provided password, then you have green light.
private void TestPassword()
{
int duration = 15; // in days
string prefix = GetExpiryDate(DateTime.Today.AddDays(duration));
string generated = GeneratePassword(prefix);
// Positive test
string testPrefix = generated.Substring(0, 8);
string testPassword = GeneratePassword(testPrefix);
if (generated != TestPassword)
return false;
// Negative test
generated[2] = '2';
generated[12] = 'b';
testPrefix = generated.Substring(0, 8);
testPassword = GeneratePassword(testPrefix);
if (generated != TestPassword)
return true;
return false;
}
Sample output password:
20110318k3X3GEDvP0LkBN6zCrkijIE+sNc=
If you can't get the hardware address, then simply use the customer's name. It won't prevent the password from being used in multiple machines, but it will ensure that the same person is using it.
Your application should have a attribute like validity for the password something like this
username password_hash validity_from Validity_end
xyz a73839$56 11-Nov-2010 12-Nov-2010
and then in your application you can validate that your password has expired or not
Generate passwords by any method you'd like (a word list, random letters, etc). Put them into some data structure, like an associative array, where you can associate a date with each password. Then you consult this data structure in the program that hands out passwords to give one out with the proper expiration date. The client program has the same list of passwords and dates, so when it gets a password, it just looks up the associated expiration date there.