public void SerializeDeserializeAppPrincipal() { AppPrincipal appPrincipal = new AppPrincipal("genericAppHandle", "genericAppKey"); string serializedAppPrincipal = appPrincipal.ToString(); AppPrincipal deserializedAppPrincipal = new AppPrincipal(serializedAppPrincipal); Assert.AreEqual(appPrincipal.AppHandle, deserializedAppPrincipal.AppHandle); Assert.AreEqual(appPrincipal.AppKey, deserializedAppPrincipal.AppKey); }
/// <summary> /// Method that generates the token descriptor of a SocialPlus session token /// </summary> /// <param name="appPrincipal">app principal</param> /// <param name="claims">token's claims</param> /// <param name="duration">lifetime of the token</param> /// <returns>token descriptor</returns> private SecurityTokenDescriptor GenerateSessionTokenDescriptor(AppPrincipal appPrincipal, Claim[] claims, TimeSpan duration) { var now = DateTime.UtcNow; return(new SecurityTokenDescriptor { TokenIssuerName = appPrincipal.ToString(), Subject = new ClaimsIdentity(claims), Lifetime = new Lifetime(null, now.Add(duration)), SigningCredentials = new SigningCredentials(this.signingKey, SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256Digest), }); }
/// <summary> /// Validate authorization header. /// The SocialPlus auth header must include a token. This token is our own session token that includes an app and a user principal /// This method: /// - validates the token /// - validates the app principal /// - validates the user principal /// </summary> /// <param name="authParameter">authorization parameter</param> /// <returns>list of principals</returns> public async Task <List <IPrincipal> > ValidateAuthParameter(string authParameter) { string token; // Parse the authorization header var authDictionary = new Dictionary <string, string>(); this.ParseAuthParameter(authParameter, authDictionary); // Get the token and validate it. authDictionary.TryGetValue("tk", out token); var principals = await this.sessionTokenManager.ValidateToken(token); // Before returning the principals, we must validate them AppPrincipal appPrincipal = null; UserPrincipal userPrincipal = null; foreach (var p in principals) { if (p is AppPrincipal) { appPrincipal = p as AppPrincipal; } else { userPrincipal = p as UserPrincipal; } } // Fire the validation tasks in parallel var task1 = this.ValidateAppKey(appPrincipal.AppKey); var task2 = this.ValidateUserPrincipal(userPrincipal, appPrincipal.AppHandle); await Task.WhenAll(task1, task2); // Is app key valid? if (task1.Result == false) { string errorMessage = $"Invalid AppKey. AppPrincipal={appPrincipal.ToString()}"; this.Log.LogException(errorMessage); } // Is user principal valid? if (task2.Result == false) { string errorMessage = $"Invalid UserPrincipal. UserPrincipal={userPrincipal.ToString()}, AppHandle={appPrincipal.AppHandle}"; this.Log.LogException(errorMessage); } return(principals); }
public async Task <IHttpActionResult> PostLinkedAccount([FromBody] PostLinkedAccountRequest request) { string className = "MyLinkedAccountsController"; string methodName = "PostLinkedAccount"; string logEntry = $"IdentityProvider = {this.UserPrincipal?.IdentityProvider}"; this.LogControllerStart(this.log, className, methodName, logEntry); // 1. Check that the auth header has no user handle. If it does, it means that the auth filter already found a user handle linked // to this credential if (this.UserHandle != null) { this.log.LogError(string.Format("User already has account linked. UserHandle: {0}", this.UserHandle)); return(this.Conflict(ResponseStrings.LinkedAccountExists)); } // 2. Validate the session token. If token is invalid we return BadRequest (400) and not unauthorized (401). List <IPrincipal> principals; try { principals = await this.sessionTokenManager.ValidateToken(request.SessionToken); } catch (Exception e) { // Catch exception and log it this.log.LogError(string.Format("Session token {0} invalid in PostLinkedAccount", request.SessionToken), e); return(this.BadRequest(ResponseStrings.SessionTokenInvalid)); } // Extract app and user principals from session token. AppPrincipal sessionTokenAppPrincipal = null; UserPrincipal sessionTokenUserPrincipal = null; foreach (IPrincipal p in principals) { if (p is AppPrincipal) { sessionTokenAppPrincipal = p as AppPrincipal; } else { sessionTokenUserPrincipal = p as UserPrincipal; } } // 3. Check that the app principal extracted from session token matches the one in the auth filter. if (sessionTokenAppPrincipal != this.AppPrincipal) { this.log.LogError($"Session token belongs to app {sessionTokenAppPrincipal.ToString()} whereas the request's token belongs to app {this.AppPrincipal.ToString()}"); return(this.BadRequest(ResponseStrings.SessionTokenInvalid)); } // 4. Check if the account is linked already. For this we use the user handle from the session token and the identity provider from the Auth header var linkedAccountEntity = await this.usersManager.ReadLinkedAccount(sessionTokenUserPrincipal.UserHandle, this.UserPrincipal.IdentityProvider); if (linkedAccountEntity != null) { this.log.LogError($"User already has account linked. UserHandle: {sessionTokenUserPrincipal.UserHandle}, IdentityProvider: {this.UserPrincipal.IdentityProvider}"); return(this.Conflict(ResponseStrings.LinkedAccountExists)); } // 5. Finally link account UserPrincipal linkedAccountUserPrincipal = new UserPrincipal(this.log, sessionTokenUserPrincipal.UserHandle, this.UserPrincipal.IdentityProvider, this.UserPrincipal.IdentityProviderAccountId); await this.usersManager.CreateLinkedAccount(ProcessType.Frontend, linkedAccountUserPrincipal); logEntry += $", SessionTokenAppHandle = {sessionTokenAppPrincipal?.AppHandle}, SessionTokenUserHandle = {sessionTokenUserPrincipal?.UserHandle}"; this.LogControllerEnd(this.log, className, methodName, logEntry); return(this.NoContent()); }
/// <summary> /// Generates a claim from an appPrincipal /// </summary> /// <param name="appPrincipal">appPrincipal input</param> /// <returns>claim</returns> private Claim GenerateAppClaim(AppPrincipal appPrincipal) { return(new Claim("iss", appPrincipal.ToString())); }