388 lines
13 KiB
C#
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
|
|
}
|
|
} |