/* * private async Task<Tokens> GetAccessTokenFromAuthCode(HttpClient httpClient, HttpContext context, ICasConfig config, string code, string scope) * { * * // get the client secret * var secret = await config.GetString("CLIENT_SECRET", CasEnv.AzureClientSecret); * * // get the response * using (var request = new HttpRequestMessage() * { * RequestUri = new Uri($"{CasEnv.AzureAuthority}/oauth2/v2.0/token"), * Method = HttpMethod.Post * }) * { * using (request.Content = new FormUrlEncodedContent(new[] { * new KeyValuePair<string, string>("client_id", CasEnv.AzureClientId), * new KeyValuePair<string, string>("client_secret", secret), * new KeyValuePair<string, string>("scope", scope), * new KeyValuePair<string, string>("code", code), * new KeyValuePair<string, string>("redirect_uri", CasEnv.RedirectUri(context.Request)), * new KeyValuePair<string, string>("grant_type", "authorization_code") * })) * { * using (var response = await httpClient.SendAsync(request)) * { * var raw = await response.Content.ReadAsStringAsync(); * if (!response.IsSuccessStatusCode) * { * throw new Exception($"GetAccessTokenFromAuthCode: HTTP {(int)response.StatusCode} - {raw}"); * } * var tokens = JsonConvert.DeserializeObject<Tokens>(raw); * return tokens; * } * } * }; * * } * * private async Task<Tokens> GetAccessTokenFromRefreshToken(HttpClient httpClient, ICasConfig config, string refreshToken, string scope) * { * * // get the client secret * var secret = await config.GetString("CLIENT_SECRET", CasEnv.AzureClientSecret); * * // get the response * using (var request = new HttpRequestMessage() * { * RequestUri = new Uri($"{CasEnv.AzureAuthority}/oauth2/v2.0/token"), * Method = HttpMethod.Post * }) * { * using (request.Content = new FormUrlEncodedContent(new[] { * new KeyValuePair<string, string>("client_id", CasEnv.AzureClientId), * new KeyValuePair<string, string>("client_secret", secret), * new KeyValuePair<string, string>("scope", scope), * new KeyValuePair<string, string>("refresh_token", refreshToken), * new KeyValuePair<string, string>("grant_type", "refresh_token") * })) * { * using (var response = await httpClient.SendAsync(request)) * { * var raw = await response.Content.ReadAsStringAsync(); * if (!response.IsSuccessStatusCode) * { * throw new Exception($"GetAccessTokenFromRefreshToken: HTTP {(int)response.StatusCode} - {raw}"); * } * var tokens = JsonConvert.DeserializeObject<Tokens>(raw); * return tokens; * } * } * }; * * } * * private async Task<Tokens> GetAccessTokenFromClientSecret(HttpClient httpClient, string clientId, string clientSecret, string scope) * { * * // get the response * using (var request = new HttpRequestMessage() * { * RequestUri = new Uri($"{CasEnv.AzureAuthority}/oauth2/v2.0/token"), * Method = HttpMethod.Post * }) * { * using (request.Content = new FormUrlEncodedContent(new[] { * new KeyValuePair<string, string>("client_id", clientId), * new KeyValuePair<string, string>("client_secret", clientSecret), * new KeyValuePair<string, string>("scope", scope), * new KeyValuePair<string, string>("grant_type", "client_credentials") * })) * { * using (var response = await httpClient.SendAsync(request)) * { * var raw = await response.Content.ReadAsStringAsync(); * if (!response.IsSuccessStatusCode) * { * throw new Exception($"GetAccessTokenFromClientSecret: HTTP {(int)response.StatusCode} - {raw}"); * } * var tokens = JsonConvert.DeserializeObject<Tokens>(raw); * return tokens; * } * } * }; * * } * * private async Task<Tokens> GetAccessTokenFromClientCertificate(HttpClient httpClient, string clientId, string token, string scope) * { * * // get the response * using (var request = new HttpRequestMessage() * { * RequestUri = new Uri($"{CasEnv.AzureAuthority}/oauth2/v2.0/token"), * Method = HttpMethod.Post * }) * { * using (request.Content = new FormUrlEncodedContent(new[] { * new KeyValuePair<string, string>("client_id", clientId), * new KeyValuePair<string, string>("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"), * new KeyValuePair<string, string>("client_assertion", token), * new KeyValuePair<string, string>("scope", scope), * new KeyValuePair<string, string>("grant_type", "client_credentials") * })) * { * using (var response = await httpClient.SendAsync(request)) * { * var raw = await response.Content.ReadAsStringAsync(); * if (!response.IsSuccessStatusCode) * { * throw new Exception($"GetAccessTokenFromClientSecret: HTTP {(int)response.StatusCode} - {raw}"); * } * var tokens = JsonConvert.DeserializeObject<Tokens>(raw); * return tokens; * } * } * }; * * } */ public override async Task Token(HttpContext context) { // get the authflow if (!context.Request.Cookies.ContainsKey("authflow")) { throw new CasHttpException(400, "authflow not provided"); } var flow = JsonConvert.DeserializeObject <CasAuthFlow>(context.Request.Cookies["authflow"]); if (context.Request.Form["state"] != flow.state) { throw new CasHttpException(400, "state does not match"); } // NOTE: google seems to throw errors on their own domain, they don't return them // verify the id_token string idRaw = context.Request.Form["id_token"]; var idToken = await VerifyTokenFromGoogle(idRaw, CasConfig.GoogleClientId, flow.nonce); // ensure the email is verified if (CasConfig.GoogleEmailMustBeVerified) { var verified = idToken.Payload.Claims.FirstOrDefault(c => c.Type == "email_verified"); if (verified.Value != "true") { throw new CasHttpException(403, "email was not verified"); } } // ICasAuthCodeReceiver: use the code to get an access token /* NOTE: I have not tested authcode with google yet * var authCodeReceiver = context.RequestServices.GetService<ICasAuthCodeReceiver>(); * if (authCodeReceiver != null) * { * string code = context.Request.Query["code"]; * Tokens last = null; * var scopes = await authCodeReceiver.GetAllScopes(); * foreach (var scope in scopes) * { * if (last == null) * { * last = await GetAccessTokenFromAuthCode(httpClient, context, config, code, "offline_access " + scope); * } * else * { * last = await GetAccessTokenFromRefreshToken(httpClient, config, last.refresh_token, "offline_access " + scope); * } * await authCodeReceiver.ReceiveAll(scope, last.access_token, last.refresh_token); * break; * } * } */ // build the claims var claims = BuildClaims(idToken); // add a sub (useful for istio) var sub = claims.FirstOrDefault(c => c.Type == "email") ?? idToken.Payload.Claims.FirstOrDefault(c => c.Type == "sub"); if (sub != null) { claims.Add(new Claim("sub", sub.Value)); } // NOTE: google does not have an oid equivalent // NOTE: google does not support role claims // apply custom claims if (ClaimsBuilder != null) { await ClaimsBuilder.AddAllClaims(idToken.Payload.Claims, claims); } // write the token cookies await WriteTokenCookies(context, claims); // redirect await Redirect(context, flow); }
public override async Task Token(HttpContext context) { // read flow, verify state and nonce if (!context.Request.Cookies.ContainsKey("authflow")) { throw new CasHttpException(400, "authflow not provided"); } var flow = JsonConvert.DeserializeObject <CasAuthFlow>(context.Request.Cookies["authflow"]); if (context.Request.Form["state"] != flow.state) { throw new CasHttpException(400, "state does not match"); } // throw error if one was returned if (context.Request.Form.ContainsKey("error_description")) { throw new CasHttpException(401, context.Request.Form["error_description"]); } // verify the id token string idRaw = context.Request.Form["id_token"]; var idToken = await VerifyTokenFromAAD(idRaw, CasConfig.AzureClientId, flow.nonce); // ICasAuthCodeReceiver: use the code to get an access token if (AuthCodeReceiver != null) { // get code string code = context.Request.Form["code"]; Tokens last = null; // get tokens for each scope var scopes = await AuthCodeReceiver.GetAllScopes(); foreach (var scope in scopes) { if (last == null) { last = await GetAccessTokenFromAuthCode(context, code, "offline_access " + scope); } else { last = await GetAccessTokenFromRefreshToken(last.refresh_token, "offline_access " + scope); } await AuthCodeReceiver.ReceiveAll(scope, last.access_token, last.refresh_token); break; } } // populate the claims from the id_token var claims = BuildClaims(idToken); // get the oid var oid = await GetOid(idToken, claims); // add a sub (useful for istio) var sub = claims.FirstOrDefault(c => c.Type == "email") ?? claims.FirstOrDefault(c => c.Type == "oid") ?? idToken.Payload.Claims.FirstOrDefault(c => c.Type == "sub"); if (sub != null) { claims.Add(new Claim("sub", sub.Value)); } // attempt to propogate roles var roles = idToken.Payload.Claims.Where(c => c.Type == "roles"); foreach (var role in roles) { claims.Add(new Claim("role", role.Value)); } // populate all application roles from the graph if (oid != null) { var assignments = await GetRoleAssignments(oid); foreach (var assignment in assignments) { foreach (var role in assignment.Roles) { claims.Add(new Claim(assignment.AppId + "-role", role)); } } } // apply custom claims if (ClaimsBuilder != null) { await ClaimsBuilder.AddAllClaims(idToken.Payload.Claims, claims); } // wrote the cookies await WriteTokenCookies(context, claims); // redirect await Redirect(context, flow); }