// // GET: /UserProfile/ public async Task <ActionResult> Index(string authError) { UserProfile profile = new UserProfile(); string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value; // Always setup the OAuth /authorize URI to use Uri redirectUri = new Uri(Request.Url.GetLeftPart(UriPartial.Authority).ToString() + "/OAuth"); string state = GenerateState(userObjectID, Request.Url.ToString()); string msoauthUri = string.Format("{0}/oauth2/authorize?resource={1}&client_id={2}&response_type=code&redirect_uri={3}&state={4}", Startup.Authority, Url.Encode(Startup.graphResourceId), Startup.clientId, Url.Encode(redirectUri.ToString()), state); ViewBag.AuthorizationUrl = msoauthUri; // Check local OAuthDataStore to see if we have previously cached OAuth bearer tokens for this user. IEnumerable <OAuthTokenSet> query = from OAuthTokenSet in model.OAuthTokens where OAuthTokenSet.userId == userObjectID && OAuthTokenSet.state == state select OAuthTokenSet; if (query.GetEnumerator().MoveNext() == false) { authError = "AuthorizationRequired"; } else { OAuthTokenSet usertokens = query.First(); profile.AccessToken = usertokens.accessToken; profile.RefreshToken = usertokens.refreshToken; profile.AccessTokenExpiry = usertokens.accessTokenExpiry; authError = null; } // Leaving this chunk of code alone, it generates the URL that the user will be redirected to when they // opt to sign in again. Per OAuth2 flow, this redirect will send the user to MS OAuth endpoint where they // will enter their creds. The resulting Authorization code is then used to get tokens. The OAuthController // will redirect users back to this controller, where we should be able to continue because the user completed // OAuth ok. if (authError != null) { profile = new UserProfile(); profile.DisplayName = " "; profile.GivenName = " "; profile.Surname = " "; ViewBag.ErrorMessage = authError; return(View(profile)); } OAuthTokenSet token = query.First(); try { // // Call the Graph API and retrieve the user's profile. // string requestUrl = String.Format( CultureInfo.InvariantCulture, Startup.graphUserUrl, HttpUtility.UrlEncode(Startup.tenant)); HttpClient client = new HttpClient(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.accessToken); HttpResponseMessage response = await client.SendAsync(request); // // Return the user's profile in the view. // if (response.IsSuccessStatusCode) { string responseString = await response.Content.ReadAsStringAsync(); UserProfile tmp = JsonConvert.DeserializeObject <UserProfile>(responseString); // Copy over only the fields recevied from GraphAPI profile.DisplayName = tmp.DisplayName; profile.GivenName = tmp.GivenName; profile.Surname = tmp.Surname; return(View(profile)); } else if (response.StatusCode == HttpStatusCode.Unauthorized) { // // If the call failed, then drop the current access token and show the user an error indicating they might need to sign-in again. model.OAuthTokens.RemoveRange(model.OAuthTokens); model.SaveChanges(); profile = new UserProfile(); profile.DisplayName = " "; profile.GivenName = " "; profile.Surname = " "; ViewBag.ErrorMessage = "AuthorizationRequired"; return(View(profile)); } else { ViewBag.ErrorMessage = "Error Calling Graph API."; return(View("Error")); } } catch { ViewBag.ErrorMessage = "Error Calling Graph API."; return(View("Error")); } }
// // This method will be invoked as a call-back from an authentication service (e.g., https://login.microsoftonline.com/). // It is not intended to be called directly, only as a redirect from the authorization request in UserProfileController. // On completion, the method will cache the refresh token and access tokens, and redirect to the URL // specified in the state parameter. // public async Task <ActionResult> Index(string code, string error, string error_description, string resource, string state) { string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier").Value; // NOTE: In production, OAuth must be done over a secure HTTPS connection. if (Request.Url.Scheme != "https" && !Request.Url.IsLoopback) { return(View("Error")); } // Ensure there is a state value on the response. If there is none, stop OAuth processing and display an error. if (state == null) { ViewBag.ErrorMessage = "Error Generating State."; return(View("Error")); } // Handle errors from the OAuth response, if any. If there are errors, stop OAuth processing and display an error. if (error != null) { return(View("Error")); } string redirectUri = ValidateState(state, userObjectID); if (redirectUri == null) { ViewBag.ErrorMessage = "Error Validating State."; return(View("Error")); } // Redeem the authorization code from the response for an access token and refresh token. // When this code completes, the user is redirected back to /UserProfile so the UserProfileController.Index // method can then fetch the tokens and use them in subsequent calls. try { // Replace this with code to get the access tokens manually string dest = "https://login.microsoftonline.com/b3aa98fb-8679-40e4-a942-6047017aa1a4/oauth2/token"; HttpWebRequest req = (HttpWebRequest)WebRequest.Create(dest); req.Method = "POST"; req.ContentType = "application/x-www-form-urlencoded"; string postData = String.Format("grant_type=authorization_code&client_id={0}&code={1}&redirect_uri={2}&client_secret={3}&resource={4}", Startup.clientId, code, new Uri(Request.Url.GetLeftPart(UriPartial.Path)), Startup.appKey, resource); System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); byte[] bytes = encoding.GetBytes(postData); req.ContentLength = bytes.Length; Stream nStream = req.GetRequestStream(); nStream.Write(bytes, 0, bytes.Length); nStream.Close(); HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); System.Runtime.Serialization.Json.DataContractJsonSerializer json = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(OAuthTokenResponse)); OAuthTokenResponse recvtoken = json.ReadObject(resp.GetResponseStream()) as OAuthTokenResponse; OAuthDataStore model = new OAuthDataStore(); string encodedState = Url.Encode(state); IEnumerable <OAuthTokenSet> query = from OAuthTokenSet in model.OAuthTokens where OAuthTokenSet.state == encodedState select OAuthTokenSet; OAuthTokenSet token = query.First(); token.accessToken = recvtoken.access_token; token.tokenType = recvtoken.token_type; token.refreshToken = recvtoken.refresh_token; token.userId = userObjectID; token.state = state; token.accessTokenExpiry = DateTime.Now.AddSeconds(Convert.ToDouble(recvtoken.expires_in)).ToUniversalTime().ToString(DateTimeFormatInfo.CurrentInfo.UniversalSortableDateTimePattern); try { model.SaveChanges(); } catch (Exception e) { throw; } return(Redirect(redirectUri)); } catch (Exception e) { return(Redirect("/UserProfile/Index?authError=token")); } }