- 
                Notifications
    You must be signed in to change notification settings 
- Fork 2
Setup Anonymous token provider with Headless Form
Headless Form API only allow API usage for authenticated user. This setup configured a sample anonymous token provider to generate a token for anonymous user based on their session ID to use for submission.
This sample modify the default JSSDK music festival sample site. Reference: https://github.com/episerver/content-headless-form-js-sdk/tree/develop/samples
Setup a token provider for users/visitors on the site that provides tokens to authorize API access.
Below is a sample builtin controller to generate token: (This should be configured with other token provider or custom token service)
    [Route("api/[controller]")]
    [ApiController]
    public class JwtTokenController : ControllerBase
    {
        private readonly string SecretKey = "6e13c68325f0379c2a6b1277e4eb9b97";
        public JwtTokenController() { }
        [HttpGet("GetJwtToken")]
        public JsonResult GetJwtToken(string username)
        {
            return new JsonResult(GenerateToken(new() { Username = username, Role = "user"}));
        }
        // To generate token
        private string GenerateToken(UserModel user)
        {
            var encryptionKey = Encoding.UTF8.GetBytes(SecretKey);
            var claims = new[]
            {
                new Claim("name", user.Username),
                new Claim("role", user.Role)
            };
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Issuer = "tsng",
                Subject = new ClaimsIdentity(claims),
                Expires = DateTime.UtcNow.AddDays(1),
            };
            var tokenHandler = new JwtSecurityTokenHandler();
            var token = tokenHandler.CreateJwtSecurityToken(tokenDescriptor);
            return tokenHandler.WriteToken(token);
        }
        private class UserModel
        {
            public string Username { get; set; }
            public string Role { get; set; }
        }
    }Update the site to use the configured token for form submission.
Sample code changed to JSSDK music festival site to interact with our builtin sample token provider:
- Configure AuthProvider to get token
- AuthProvider.js
 
const jose = require('jose');
class AuthProvider {
    // Secret key used to sign the JWT token
    constructor() {}
    generateSessionId() {
        const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        const length = 20; // You can adjust the length of the session ID as needed
        let sessionId = '';
        for (let i = 0; i < length; i++) {
            const randomIndex = Math.floor(Math.random() * characters.length);
            sessionId += characters[randomIndex];
        }
        return `anonymous_${sessionId}`;
    }
    async generateJwtToken(username, authenticated) {
        if (!username) {
            username = this.generateSessionId();
            authenticated = false;
        } else {
            authenticated = authenticated ?? true;
        }
        const payload = {
            username: username,
            authenticated: authenticated,
            exp: Math.floor(Date.now() / 1000) + (60 * 60 * 24)  // Token expiration time (1 day)
        };
        const token = await fetch(`http://domain:port/api/JwtToken/GetJwtToken?username=${username}`);
        sessionStorage.setItem('user', JSON.stringify({ access_token: await token.json(), ...payload }));
        return token;
    }
    getUser() {
        return new Promise((resolve, reject) => {
            const user = sessionStorage.getItem('user');
            user ? resolve(JSON.parse(user)) :
                this.generateJwtToken(null).then(data => resolve(JSON.parse(sessionStorage.getItem('user'))));
        });
    }
    signinRedirect(args) {
        this.generateJwtToken("authenticated_user");
        window.location.href = args.state;
    }
    signoutRedirect() {
        this.generateJwtToken(null);
        window.location.reload();
    }
    refreshAccessToken() {
        this.getUser().then(user => {
            return this.generateJwtToken(user.username, user.authenticated);
        });
    }
}
export default AuthProvider;- AuthService.js
import AuthProvider from './authProvider';
class AuthService {
  constructor() {
    this.authProvider = new AuthProvider();
  }
  getUser() {
    return new Promise( (resolve, reject) => {
      this.authProvider.getUser().then(user => {
        console.log(`JWT Bearer token for ${user.username}: ${user.access_token}`)
        resolve({...user, expired: Math.floor(Date.now() / 1000) > user.exp}) 
      })
    })
  }
  login() {
    const args = {
      state: window.location.href,
    };
    return this.authProvider.signinRedirect(args);
  }
  logout() {
    return this.authProvider.signoutRedirect();
  }
  getAccessToken() {
    return this.authProvider.getUser();
  }
  refreshAccessToken() {
    return this.authProvider.generateJwtToken()
  }
}
const authService = new AuthService();
export default authService;- Configure Header.tsx to pass token with API request
        authService.getUser().then((user: any) => {
            if(user && user.expired) {
                authService.refreshAccessToken().then((_: any) => {
                    authService.getUser().then((_user: any) => { user = _user })
                })
            }
            if (user && !user.expired) {
                setIsLoggedIn(user.authenticated);
                setUsername(user.username || "");
                formCache.set<string>(FormConstants.FormAccessToken, user.access_token);
            }
        });Modify startup.cs file and add these lines inside and at the end of the Configure method:
// Register the Optimizely Headless Form API Services
services.AddOptimizelyFormsService(options =>
{
    options.EnableOpenApiDocumentation = true;
    options.FormCorsPolicy = new FormCorsPolicy
    {
        AllowOrigins = new string[] { https://domain.com }, //Enter '*' to allow any origins, multiple origins separate by comma
        AllowCredentials = true
    };
    options.OpenIDConnectClients.Add(new()
    {
        Authority = ClientEndpoint,
        EncryptionKeys = new List<SecurityKey>{ new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)) }
        SigningKeys = new List<SecurityKey>{ new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)) }
    });
});