Authentication
APD provides built-in authentication for portal users, allowing secure access to protected content and personalized experiences.
Authentication Overview
How It Works
- User navigates to a protected action
- APD redirects to Login action
- User enters credentials
- APD validates against User table
- Session marked as authenticated
- User redirected to original action
Session Management
- Sessions stored in APD Session table
- Session ID passed via cookie or URL
- Sessions have configurable timeout
- Authentication state persists across requests
Login Action Setup
Creating a Login Action
- Create a new action
- Set Login Action = Yes
- Create the login template
- Compile the action
Login Action Properties
| Property | Value |
|---|---|
| Login Action | Yes |
| Require Authentication | No |
| Type | HTML or Html Script |
Login Template
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<div class="login-container">
<h1>Login</h1>
<!-- <?loginerror?> outputs the login error message (empty when no error). -->
<div class="error"><?loginerror(.)?></div>
<form method="post">
<input type="hidden" name="action" value="LOGIN">
<div class="field">
<label>Username</label>
<input type="text" name="username" required autofocus>
</div>
<div class="field">
<label>Password</label>
<input type="password" name="password" required>
</div>
<button type="submit">Login</button>
</form>
<div class="links">
<a href="<?action(FORGOT-PASSWORD)?>">Forgot Password?</a>
<a href="<?action(REGISTER)?>">Create Account</a>
</div>
</div>
</body>
</html>
APD does not provide
<?if(userblocked)?>or any other built-in user-blocked tag in the template language. The login flow itself enforces the lockout: when a user has been blocked, the next login attempt fails and the message is carried in<?loginerror?>— there is no separate template branch to render. If you need a custom "account locked" page, send the user there from a Codeunit or Script action after detecting the block.
Form Parameters
Required Parameters
| Parameter | Description |
|---|---|
| username | User's login ID or email |
| password | User's password |
| action | The login action code |
Optional Parameters
| Parameter | Description |
|---|---|
| company | Company code (multi-company) |
| actionurl | Return action after login |
| Alternative to username |
Password Handling
Password Storage
- Passwords are never stored in plain text
- Hashed using secure algorithm
- Hash stored in "Hashed Password" field
Password Validation
- User enters password
- APD hashes the input
- Compared against stored hash
- Match = success, no match = failure
Password Attempts
- Failed attempts tracked per user
- Configurable maximum attempts
- User locked after max attempts
Login Process Flow
┌──────────────┐
│ User submits │
│ login form │
└──────┬───────┘
▼
┌──────────────┐
│ APD receives │
│ credentials │
└──────┬───────┘
▼
┌──────────────┐ No ┌──────────────┐
│ User exists? ├────────────►│ Show error │
└──────┬───────┘ │ Wrong userid │
│ Yes └──────────────┘
▼
┌──────────────┐ Yes ┌──────────────┐
│ User blocked?├────────────►│ Show blocked │
└──────┬───────┘ │ message │
│ No └──────────────┘
▼
┌──────────────┐ No ┌──────────────┐
│ Password ├────────────►│ Increment │
│ matches? │ │ attempts │
└──────┬───────┘ └──────────────┘
│ Yes
▼
┌──────────────┐
│ Create │
│ session │
└──────┬───────┘
▼
┌──────────────┐
│ Redirect to │
│ requested │
│ action │
└──────────────┘
Logout
Logout Action
Create a logout action or use built-in logout:
<a href="<?action(LOGOUT)?>">Logout</a>
Logout Process
- Session cleared
- Authentication state removed
- User redirected to default action
Logout Template
APD does not provide a <?redirect?> template tag. The <?logout?> tag invalidates the session, and the configured Login Action handles unauthenticated users on subsequent requests. A typical post-logout template just confirms and offers a path back to login:
<?logout?>
<h1>You have been logged out</h1>
<a href="<?action(LOGIN)?>">Login Again</a>
Session Information
Available in Templates
| Tag | Description |
|---|---|
<?userid?> |
Logged-in user ID |
<?company?> |
User's company |
<?sessionid?> |
Current session ID |
<?no?> |
User's BC entity number |
<?usertype(N)?>...<?endif?> |
Conditional block — opens only when the session's BC Type matches N (1=Customer, 2=Vendor, 3=Contact, 4=Sales Person). There is no separate <?navisiontype?> tag. |
Authentication Check
<?auth?>
<p>Welcome, <?userid?>!</p>
<?else?>
<a href="<?action(LOGIN)?>">Please login</a>
<?endif?>
Multi-Company Login
Company Selection
If multiple companies are available:
<form method="post">
<input type="text" name="username" required>
<input type="password" name="password" required>
<select name="company">
<option value="CRONUS">CRONUS International</option>
<option value="FABRIKAM">Fabrikam Inc</option>
</select>
<button type="submit">Login</button>
</form>
Company from User
If user is assigned to specific company, company selection is not needed.
Email Login
Using Email Instead of Username
APD supports email-based login:
<input type="email" name="email" placeholder="your@email.com">
APD will look up the user by email field.
Remember Return URL
Capturing Return URL
You don't enforce login from inside the template — set Require Authentication on the action card instead. When an unauthenticated user hits that action, APD redirects to the configured Login Action and remembers the original action URL in the session (actionurl parameter). After successful login, APD returns the user to the original action automatically; no template-level redirect is needed.
Using Return URL
If your login template needs to preserve a return target (e.g. for a custom flow), pass it through a hidden field bound to the existing actionurl parameter:
<!-- In login template -->
<input type="hidden" name="actionurl" value="<?parm(actionurl)?>">
Error Messages
Available Error Information
| Tag | Description |
|---|---|
<?loginerror?> |
Login error message — empty when no error is set; when called with a second argument like <?loginerror(.)?> returns the actual Session."Error Status Text". |
There is no
<?errortext?>tag orerrortext()expression function — APD surfaces login errors through<?loginerror?>only.
Error Display
<?loginerror?> already returns empty when there is no error, so a guard <?if?> is unnecessary. The cleanest pattern is to render the surrounding element unconditionally and use CSS to hide it when its content is empty:
<div class="alert alert-danger"><?loginerror(.)?></div>
Lockout state is not exposed as a separate template flag. After a failed login attempt — including a "user is locked out" failure — <?loginerror?> carries the user-facing reason.
Security Considerations
Password Security
- Use strong password requirements
- Implement password reset flow
- Consider password expiration policies
Brute Force Protection
- Configure maximum password attempts
- Implement account lockout
- Consider CAPTCHA after failures
Session Security
- Use HTTPS for all traffic
- Implement session timeout
- Clear sessions on logout
CSRF Protection
- Include CSRF tokens in forms
- Validate tokens on submit
Best Practices
User Experience
- Clear error messages
- Show remaining attempts
- Provide password reset option
- Remember intended destination
Security
- Always use HTTPS
- Don't expose user enumeration
- Log authentication events
- Implement rate limiting
Maintenance
- Review locked accounts
- Monitor failed attempts
- Update password policies