summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth/InfoCard/Token/Token.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth/InfoCard/Token/Token.cs')
-rw-r--r--src/DotNetOpenAuth/InfoCard/Token/Token.cs177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth/InfoCard/Token/Token.cs b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
new file mode 100644
index 0000000..09100d9
--- /dev/null
+++ b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
@@ -0,0 +1,177 @@
+//-----------------------------------------------------------------------
+// <copyright file="Token.cs" company="Andrew Arnott, Microsoft Corporation">
+// Copyright (c) Andrew Arnott, Microsoft Corporation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.InfoCard {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.IdentityModel.Claims;
+ using System.IdentityModel.Policy;
+ using System.IO;
+ using System.Security.Cryptography.X509Certificates;
+ using System.Text;
+ using System.Xml;
+ using System.Xml.XPath;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The decrypted token that was submitted as an Information Card.
+ /// </summary>
+ [ContractVerification(true)]
+ public class Token {
+ /// <summary>
+ /// Backing field for the <see cref="Claims"/> property.
+ /// </summary>
+ private IDictionary<string, string> claims;
+
+ /// <summary>
+ /// Backing field for the <see cref="UniqueId"/> property.
+ /// </summary>
+ private string uniqueId;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Token"/> class.
+ /// </summary>
+ /// <param name="tokenXml">Xml token, which may be encrypted.</param>
+ /// <param name="audience">The audience.</param>
+ /// <param name="decryptor">The decryptor to use to decrypt the token, if necessary..</param>
+ /// <exception cref="InformationCardException">Thrown for any problem decoding or decrypting the token.</exception>
+ internal Token(string tokenXml, Uri audience, TokenDecryptor decryptor) {
+ Contract.Requires(tokenXml != null && tokenXml.Length > 0);
+ Contract.Requires(audience != null);
+ Contract.Requires(decryptor != null || !IsEncrypted(tokenXml));
+ ErrorUtilities.VerifyNonZeroLength(tokenXml, "tokenXml");
+ ErrorUtilities.VerifyArgumentNotNull(audience, "audience");
+
+ byte[] decryptedBytes;
+ string decryptedString;
+
+ using (XmlReader tokenReader = XmlReader.Create(new StringReader(tokenXml))) {
+ if (IsEncrypted(tokenReader)) {
+ ErrorUtilities.VerifyArgumentNotNull(decryptor, "decryptor");
+ decryptedBytes = decryptor.DecryptToken(tokenReader);
+ decryptedString = Encoding.UTF8.GetString(decryptedBytes);
+ } else {
+ decryptedBytes = Encoding.UTF8.GetBytes(tokenXml);
+ decryptedString = tokenXml;
+ }
+ }
+
+ this.Xml = new XPathDocument(new StringReader(decryptedString)).CreateNavigator();
+ this.AuthorizationContext = TokenUtility.AuthenticateToken(this.Xml.ReadSubtree(), audience);
+ }
+
+ /// <summary>
+ /// Gets the AuthorizationContext behind this token.
+ /// </summary>
+ public AuthorizationContext AuthorizationContext { get; private set; }
+
+ /// <summary>
+ /// Gets the the decrypted token XML.
+ /// </summary>
+ public XPathNavigator Xml { get; private set; }
+
+ /// <summary>
+ /// Gets the UniqueID of this token, usable as a stable username that the user
+ /// has already verified belongs to him/her.
+ /// </summary>
+ /// <remarks>
+ /// By default, this uses the PPID and the Issuer's Public Key and hashes them
+ /// together to generate a UniqueID.
+ /// </remarks>
+ public string UniqueId {
+ get {
+ if (string.IsNullOrEmpty(this.uniqueId)) {
+ this.uniqueId = TokenUtility.GetUniqueName(this.AuthorizationContext);
+ }
+
+ return this.uniqueId;
+ }
+ }
+
+ /// <summary>
+ /// Gets the hash of the card issuer's public key.
+ /// </summary>
+ public string IssuerPubKeyHash {
+ get { return TokenUtility.GetIssuerPubKeyHash(this.AuthorizationContext); }
+ }
+
+ /// <summary>
+ /// Gets the Site Specific ID that the user sees in the Identity Selector.
+ /// </summary>
+ public string SiteSpecificId {
+ get {
+ Contract.Requires(this.Claims.ContainsKey(WellKnownClaimTypes.Ppid));
+ ErrorUtilities.VerifyOperation(this.Claims.ContainsKey(WellKnownClaimTypes.Ppid), InfoCardStrings.PpidClaimRequired);
+ return TokenUtility.CalculateSiteSpecificID(this.Claims[WellKnownClaimTypes.Ppid]);
+ }
+ }
+
+ /// <summary>
+ /// Gets the claims in all the claimsets as a dictionary of strings.
+ /// </summary>
+ public IDictionary<string, string> Claims {
+ get {
+ if (this.claims == null) {
+ this.claims = this.GetFlattenedClaims();
+ }
+
+ return this.claims;
+ }
+ }
+
+ /// <summary>
+ /// Determines whether the specified token XML is encrypted.
+ /// </summary>
+ /// <param name="tokenXml">The token XML.</param>
+ /// <returns>
+ /// <c>true</c> if the specified token XML is encrypted; otherwise, <c>false</c>.
+ /// </returns>
+ [Pure]
+ internal static bool IsEncrypted(string tokenXml) {
+ Contract.Requires(tokenXml != null);
+ ErrorUtilities.VerifyArgumentNotNull(tokenXml, "tokenXml");
+
+ using (XmlReader tokenReader = XmlReader.Create(new StringReader(tokenXml))) {
+ return IsEncrypted(tokenReader);
+ }
+ }
+
+ /// <summary>
+ /// Determines whether the specified token XML is encrypted.
+ /// </summary>
+ /// <param name="tokenXmlReader">The token XML.</param>
+ /// <returns>
+ /// <c>true</c> if the specified token XML is encrypted; otherwise, <c>false</c>.
+ /// </returns>
+ private static bool IsEncrypted(XmlReader tokenXmlReader) {
+ Contract.Requires(tokenXmlReader != null);
+ ErrorUtilities.VerifyArgumentNotNull(tokenXmlReader, "tokenXmlReader");
+ return tokenXmlReader.IsStartElement(TokenDecryptor.XmlEncryptionStrings.EncryptedData, TokenDecryptor.XmlEncryptionStrings.Namespace);
+ }
+
+ /// <summary>
+ /// Flattens the claims into a dictionary
+ /// </summary>
+ /// <returns>A dictionary of claim type URIs and claim values.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call.")]
+ [Pure]
+ private IDictionary<string, string> GetFlattenedClaims() {
+ var flattenedClaims = new Dictionary<string, string>();
+
+ foreach (ClaimSet set in this.AuthorizationContext.ClaimSets) {
+ foreach (Claim claim in set) {
+ if (claim.Right == Rights.PossessProperty) {
+ flattenedClaims.Add(claim.ClaimType, TokenUtility.GetResourceValue(claim));
+ }
+ }
+ }
+
+ return flattenedClaims;
+ }
+ }
+}