The identity provider security pattern is a pattern that is employed just at the name suggests, it provides an identity of a subject (user/automated system/job) or what have you to the entire larger system. The identity provider pattern is built on a simpler pattern known as the circle of trust. The identity pattern is used in many systems to provide identity to user identity from a trusted source to another source such as between two web sites within the same organization without requiring the users to log in again & again.
Identity Provider By Example
The OAUTH
concept is a big example of an identity provider, the basic premise of the identity provider is such a number entities trust the identity provider, which may be a component within the system or a standalone web service. Once a subject authenticates against one of the entities, the identity provider provides some sort of identity back to the subject. When the subject wants to authenticate again, or against a different entity within the same domain, the subject only needs to provide the identity it was given after the first authentication. The second entity then validates the identity provided after the first authentication against the identity provider because all entities within the system or domain trust the entity provider. If the identity provider validates the identity, then the subject is considered authenticated, and allowed to go about their business.
Conceptually and graphically, the identity provider pattern looks like this:
In my example, I implemented the pattern using a certificate object for providing the identity. I chose this because typically credentials are used for providing identity. While credentials are bound to an identity, they are not fully one’s identity and many other things can be considered, as part of a user’s identity. Consider your name, your name is like your credentials. It’s how you identify yourself and prove to others who you are. Your name however does not make up your entire identity. So too, an identity can be a number of data points or pieces of information to fully identify where and who you are, or what application is using the service and providers.
Identifying the Players
In the example, the major players are user, the user is a subject who wants to access a particular resource stored on the server. There is the server which is guarding the sources and authenticating the various users as needed. The last player is the identity provider. The server trusts the identity provider. In the example, after the user authenticates against the server the first time, the identity provider provides the server with an identity which the server sends back to the user in the response. The subject, then grabs that certificate (which represents an identity). And provides the server with the certificate for subsequent requests. The server then provides certificate to the identity provider, and the identity provider tells the server that this user is acceptable because their identity has been verified, and therefore the user/subject does not need to provider credentials.
If in this example, there were multiple servers serving up major resources from different websites, etc. The user would only need to authenticate against the first website, the subsequent servers & applications trust the identity provider to verify that the user is who they claim to be by nature of the identity.
Identity Example
This example starts off with a subject making a request for a resource from the website such as this:
std::unique_ptr rr(new ResourceRequest("localhost:1010","payload?user=Foo&pass=Bar"));
Response resp = rr->makeRequest();
std::cout << resp.getResponseData() << std::endl;
Here, the subject is making a request for the payload resource, passing in their user name and password Note this is not a secure way to pass credentials. This request is going through to localhost on port 1010.
The server class is defined as:
class SERVER_API AppServer {
public:
AppServer(void);
virtual ~AppServer();
void StartServer();
void StopServer();
private:
Request createRequestObj(const std::string&);
std::string getResource(const std::string&);
std::string serializeResponse(const Response&);
private:
std::unique_ptr m_serverSocket;
std::shared_ptr m_identityProvider;
};
The certificate which is going to function as our identity looks like:
class CERTIFICATE_API Certificate {
public:
Certificate(void){;}
Certificate(const std::string& user);
virtual ~Certificate(){;}
const std::string& getUser() const {return m_user;}
time_t getIdate() const {return m_issueDate;}
time_t getEdate() const {return m_expireDate;}
const std::string& getIssueDate() const {return m_strIssueDate;}
const std::string& getExpiryDate() const {return m_strExpireDate;}
const std::string& getSignature() const {return m_issueSignature;}
const int getCertId() const {return m_id;}
virtual std::string getHash() const {return m_issueSignature;}
friend class CertificateSigner;
private:
friend class boost::serialization::access;
template
void serialize(Archive & ar, const unsigned int version)
{
ar & m_id;
ar & m_user;
ar & m_strIssueDate;
ar & m_strExpireDate;
ar & m_issueDate;
ar & m_expireDate;
ar & m_issueSignature;
}
private:
void signCert(const std::string&);
time_t initExpires();
private:
int m_id;
std::string m_user;
time_t m_issueDate;
time_t m_expireDate;
std::string m_issueSignature;
static int certNumber;
m_strIssueDate;
m_strExpireDate;
};
This is a really simple certificate that contains the users’ id and provides a bunch of information about when the certificate was created, when it expires and its signature. (HINT: This might be a really simple version of an SSL certificate).
When the server receives a request, the first thing the server attempts to do is authenticate the user.
bool Authenicator::isAuthenticated(const Request& r, std::shared_ptr provider)
if (!canSkipPwCheck(r, provider))
{
std::string reqData = r.getRequestData();
std::string user = UriUtil::getUserName(reqData);
std::string pass = UriUtil::getPassword(reqData);
if (user.compare("Foo") == 0 && pass.compare("Bar") ==0)
{
}
else
{return true;
}
return true;
}
return false;
}
bool Authenicator::canSkipPwCheck(const Request& r, std::shared_ptr provider)
{
if (r.getCertificate().getUser().compare("") != 0)
{
if (provider->isValidCertificate(r.getCertificate()))
{
return true;
}
}
return false;
}
When the server attempts to authenticate the user, the first thing the server determines is whether the user even needs to provide a user name and password. If the user has provided a valid certificate, then they are not required to provide a user name and password. If the user has not provided a valid certificate, they are required to provide a user name and password to be considered authenticated against this server. It is up to the identity provider to determine if the user has provided a valid certificate(identity) or not. The Identity
provider in our example is really simple.
class IdentProvider
{
public:
IdentProvider();
virtual ~IdentProvider(){;}
Certificate provideIdentity(const Request& r);
bool isValidCertificate(const Certificate& cert);
private:
std::unique_ptr m_certSigner;
};
As you can see, the identity provider has two jobs, to provide a certificate identity & to validate that an identity is valid. To understand if a certificate identity is valid, we must first consider how a certificate identity is created.
Certificate IdentProvider::provideIdentity(const Request& r)
{
std::string user = UriUtil::getUserName(r.getRequestData());
std::shared_ptr pCert (new Certificate(user));
m_certSigner->SignCertificate(pCert);
return *(pCert.get());
}
Certificate::Certificate(const std::string& user)
{
m_issueDate = TimeUtil::getCurrentTime();
m_expireDate = initExpires();
m_user = user;
certNumber++;
m_id = certNumber;
}
time_t Certificate::initExpires()
{
return TimeUtil::getExpireTime(0,10,0);
}
void CertificateSigner::SignCertificate(std::shared_ptr cert)
{
std::stringstream ss;
ss << cert->getUser() << "|" << cert->getCertId() <<
"|" << cert->getIssueDate() << "|" << "|"
<< cert->getExpiryDate() << "|" << cert->getIdate() << "|"
<< cert->getEdate() << "|" << m_secret;
std::string combStr = ss.str();
std::unique_ptr pHasher(new Hasher());
cert->signCert(pHasher->calcHash(combStr));
}
When the certificate is first created, the user’s user name is extracted from the request & and used as the user id owning the certificate. The certificate is then set to expire from 10 minutes after it was created. This completes the certificate creation, the last thing that is required is the signing of the certificate. The certificate has to be signed by the identity provider, so that when the identity provider receives the certificate back, it can verify that not only was it the one that issued the certificate identity, but no data has been tampered with. If someone attempted to change when the certificate expired along the way, this would result in an invalid certificate requiring the user to log in again. Now that we understand how a certificate was created & signed, we can consider how the identity provider verifies that the certificate is valid.
bool IdentProvider::isValidCertificate(const Certificate& cert)
{
if (cert.getUser().compare("") != 0)
{
if (cert.getEdate() > TimeUtil::getCurrentTime())
{
std::stringstream ss;
ss << cert.getUser() <<
"|" << cert.getCertId() <<
"|" << cert.getIssueDate() <<
"|" << "|" << cert.getExpiryDate()
<< "|" << cert.getIdate() <<
"|" << cert.getEdate()<<
"|" << m_certSigner->getSecret();
std::string str = ss.str();
std::unique_ptr pHash(new Hasher());
std::string hashedStr = pHash->calcHash(str);
if (cert.getHash().compare(hashedStr) == 0)
{
return true;
}
}
}
return false;
}
A certificate is considered valid if it is not empty & hasn’t expired, lastly if the signature matches a reproduced signature, this verifies that the certificate hasn’t been altered in anyway and therefore is valid. Hashing like this is not the most secure way of this type of validation, however it’s simple and easy for the example.
Now the server knows if the user is authenticated or not, and able to access the resource.
if (Authenicator::isAuthenticated(req, m_identityProvider))
{
std::string respData = getResource(req.getRequestData());
resp = Response(respData, req.getCertificate());
if (!m_identityProvider->isValidCertificate(req.getCertificate()))
{
Certificate cert = m_identityProvider->provideIdentity(req);
resp.updateCertificate(cert);
}
}
std::string replyData = serializeResponse(resp);
The server crafts a response with the original certificate the user sent in, because if it’s still valid, the user should be able to reuse it, if the certificate is invalid, then it’s updated with a certificate that is valid. This ensures that a user couldn’t log in once, and then never have to provide their credentials again by constantly getting a newer certificate that never expired.
The very next request the user makes, either to this system or another system on the domain…looks like:
std::unique_ptr pSecondRequest(new ResourceRequest("localhost:1010","TestPayLoad?"));
pSecondRequest->addCredential(resp.getCertificate());
Response secondResp =pSecondRequest->makeRequest();
std::cout << secondResp.getResponseData() << std::endl;
In this example, the user is not required to provide a user name or password and authenticate because the request provides the identity issued back to the user on the previous request. The server will now recognize this identity certificate, and authenticate the user against the identity provider and allow the user to have access to the resource.
The post Identity Provider Security Pattern appeared first on Security Synergy.