Files
xSaherElmApi/Controllers/Account/AccountController+Authentication.cs
T
2026-03-22 14:09:53 +03:30

388 lines
13 KiB
C#

using System;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using xIdentityHelper;
using xCommons.Attributes;
using xCommons.Extensions;
using xExceptions.Constants;
using xIdentityModels.Models;
using xModels.Dtos;
using static xIdentityHelper.XApiScopeHelper;
namespace xApi.Controllers {
public partial class AccountController {
//
#region Authentication Actions ...
/// <summary>
/// Retrieve OAuth Discovery Document
/// </summary>
/// <returns>an instance of <see>DiscoveryDocumentResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpGet ("DiscoveryDocument")]
public async Task<ActionResult<DiscoveryDocumentResponse>> GetDiscoveryDocument () {
//
try {
//
var result = await identityProvider
.RequestDiscoveryDocument ();
//
// Ceck Response Result ...
if (result.IsError) {
XException.InvalidConfiguration.Throw ();
}
//
return Ok (result
.ToDynamicObject ());
} catch (Exception ex) {
//
var result = GetExceptionActionResult (ex);
return result;
}
}
/// <summary>
/// Request AccessToken for Specific XApiScope
/// </summary>
/// <param name="scope">a member of <see>XApiScope</see></param>
/// <returns>an instance of <see>XTokenResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost ("RequestScopeAccessToken")]
public async Task<ActionResult<XTokenResponse>> RequestScopeAccessToken (
[FromHeader] XApiScope scope
) {
//
// Do Action ...
try {
//
// Validate Args ...
var scopeName = string.Empty;
try {
scopeName = scope.GetStringValue ();
} catch { }
ValidationProvider.NotEmpty (scopeName);
//
var result = await identityProvider
.RequestScopeAccessToken (scopeName);
//
return Ok (result
.ToDynamicObject ());
} catch (Exception ex) {
//
var result = GetExceptionActionResult (ex);
return result;
}
}
/// <summary>
/// Authenticate User
/// </summary>
/// <remarks>
/// Sample request:
///
/// {
/// "password": "",
/// "userSelectBy": "",
/// }
///
/// </remarks>
/// <param name="model">User Login Required Info, an instance of <see>XLoginRequest</see></param>
/// <returns>an instance of <see>XTokenResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost ("Authenticate")]
public async Task<ActionResult<XTokenResponse>> Authenticate (
[FromBody] XLoginRequest model
) {
try {
//
// Validate Args ...
if (!ModelState.IsValid) {
XException.InvalidArgs.Throw ();
}
await ValidationProvider
.GroupValidationBuilder ()
.AddNotNull (model)
.AddNotEmpty (
model.UserSelectBy,
model.Password
)
.ValidateGroupAsync ();
//
var result = await identityProvider
.Authenticate (model);
//
return Ok (result
.ToDynamicObject ());
} catch (Exception ex) {
//
var result = GetExceptionActionResult (ex);
return result;
}
}
/// <summary>
/// Authenticate User
/// </summary>
/// <remarks>
/// Sample request:
///
/// {
/// "language": "fa-IR"
/// "password": "",
/// "userSelectBy": "",
/// "device": {
/// "os":"Mac",
/// "browser":"Chrome",
/// "osVersion":"mac-os-x-15",
/// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36",
/// "deviceType":3,
/// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]",
/// "token":"96f400dd9aa5c57a8cec9d6f4775bad3"
/// }
/// }
///
/// </remarks>
/// <param name="model">User Login Required Info, an instance of <see>XLoginRequest</see></param>
/// <returns>an instance of <see>XLoginResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost ("Login")]
public async Task<ActionResult<XLoginResponse>> Login (
[FromBody] XLoginRequest model
) {
try {
//
// Validate Args ...
if (!ModelState.IsValid) {
XException.InvalidArgs.Throw ();
}
await ValidationProvider
.GroupValidationBuilder ()
.AddNotNull (
model,
model.Device)
.AddNotEmpty (
model.Language,
model.UserSelectBy,
model.Password
)
.ValidateGroupAsync ();
//
var result = await identityProvider
.Login (model);
//
return Ok (result
.ToDynamicObject ());
} catch (Exception ex) {
//
var result = GetExceptionActionResult (ex);
return result;
}
}
/// <summary>
/// Logout User
/// </summary>
[RequireXPowered]
[HttpPost ("Logout")]
[Authorize (Policy = XPolicies.User)]
public async Task<ActionResult> Logout () {
try {
//
var tokens = await RetrieveTokensAsXTokenResponse ();
await identityProvider
.Logout (tokens);
//
return Ok ();
} catch (Exception ex) {
//
var result = GetExceptionActionResult (ex);
return result;
}
}
/// <summary>
/// Refresh Expired Tokens
/// </summary>
/// <remarks>
/// in addition to AccessToken, you had to pass RefreshToken due to Headers
/// </remarks>
/// <returns>an instance of <see>XTokenResponse</see></returns>
[AllowAnonymous]
[RequireXPowered]
[HttpPost ("RefreshTokens")]
public async Task<ActionResult<XTokenResponse>> RefreshTokens () {
//
try {
//
// Retrieve Toke Response ...
var model = await RetrieveTokensAsXTokenResponse ();
//
// Validate Args ...
ValidationProvider
.GroupValidationBuilder ()
.AddNotNull (model)
.AddNotEmpty (
model.AccessToken,
model.RefreshToken)
.ValidateGroup ();
//
var result = await identityProvider
.RefreshTokens (model);
//
return Ok (result
.ToDynamicObject ());
} catch (Exception ex) {
//
var result = GetExceptionActionResult (ex);
return result;
}
}
/// <summary>
/// Validate a Revision Checksum
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost ("Validate")]
[Authorize (Policy = XPolicies.EnabledUser)]
public async Task<ActionResult<bool>> Validate (
[FromBody] XValidateRevisionRequest model
) {
//
// Do Action ...
try {
//
// Validate Args ...
await ValidationProvider
.GroupValidationBuilder ()
.AddNotNull (model)
.AddNotEmpty (model.Revision)
.ValidateGroupAsync ();
//
// Get Request ...
var tokens = await RetrieveTokensAsXTokenResponse ();
var result = tokens.ValidateRevisionChecksum (
model.Revision,
identityProvider.RevisionSecretKey
);
//
// Return Result
return Ok (result);
} catch (Exception ex) {
//
var result = GetExceptionActionResult (ex);
return result;
}
}
#endregion
//
#region Password Actions ...
/// <summary>
/// Change a User's Password ...
/// </summary>
/// <remarks>
/// for this action you have to provide:
/// - Lang: client device currently used locale, such as: en-US.
/// - Password: user's current active password.
/// - NewPassword: user's new password to change.
/// - Device: user's client device which is an instance of XDevice.
/// - ReturnUrl: client application Login URL.
///
/// Sample request:
///
/// {
/// "lang": "fa-IR",
/// "password": ""
/// "newPassword": "",
/// "returnUrl": "http://localhost/login",
/// "device": {
/// "os":"Mac",
/// "browser":"Chrome",
/// "osVersion":"mac-os-x-15",
/// "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36",
/// "deviceType":3,
/// "identifier":"[Mac]-[mac-os-x-15]-[3]-[Chrome]-[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) x-framework-test/0.0.0 Chrome/87.0.4280.141 Electron/11.2.0 Safari/537.36]",
/// "token":"96f400dd9aa5c57a8cec9d6f4775bad3"
/// }
/// }
///
/// </remarks>
/// <param name="model">an instance of <see>XActionRequest</see> class which provider requirement for action</param>
/// <returns></returns>
[RequireXPowered]
[HttpPost ("ChangePassword")]
[Authorize (Policy = XPolicies.User)]
[Authorize (Policy = XPolicies.EnabledUser)]
public async Task<ActionResult> ChangePassword (
[FromBody] XActionRequest model
) {
//
// Do Action ...
try {
//
// Validate Args ...
if (!ModelState.IsValid) {
XException.InvalidArgs.Throw ();
}
await ValidationProvider
.GroupValidationBuilder ()
.AddNotNull (
model,
model.Device
)
.AddNotEmpty (
model.Lang,
model.Password,
model.NewPassword,
model.ReturnUrl
)
.ValidateGroupAsync ();
//
// Get User Select By ...
var userSelectByParam = GetUserSelectByParam (
model,
forceNotNull : false
);
if (userSelectByParam.IsNullOrEmpty ()) {
userSelectByParam = User.Identity.Name;
}
ValidationProvider.NotEmpty (userSelectByParam);
//
// Get Request ...
var tokens = await RetrieveTokensAsXTokenResponse ();
await identityProvider
.ChangePassword (tokens, model);
//
return Ok ();
} catch (Exception ex) {
//
var exResult = GetExceptionActionResult (ex);
return exResult;
}
}
#endregion
}
}